import logging
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form
from sqlalchemy.orm import Session
from sqlalchemy import desc, or_

from app.api.v1 import deps
from app.models.user import User
from app.models.client_distribution import ClientDistribution, ClientVersion
from app.schemas.client_distribution import (
    ClientDistributionCreate,
    ClientDistributionUpdate,
    ClientDistributionResponse,
    ClientDistributionList,
    ClientVersionCreate,
    ClientVersionUpdate,
    ClientVersionResponse,
    ClientVersionList,
    DistributionPlatform,
)
from app.core.minio import minio_storage
from app.core.version import version_sort_key, validate_version_code
from app.core.distribution_share import encode_distribution_share_id

router = APIRouter()
logger = logging.getLogger(__name__)

# 安装包允许类型
ALLOWED_PACKAGE_TYPES = {
    "application/vnd.android.package-archive",  # APK
    "application/octet-stream",  # IPA, generic binary
}
MAX_PACKAGE_SIZE = 500 * 1024 * 1024  # 500MB

# 图标允许类型
ALLOWED_ICON_TYPES = {"image/jpeg", "image/png", "image/gif", "image/webp"}
MAX_ICON_SIZE = 2 * 1024 * 1024  # 2MB


def _resolve_icon_url(icon_url: Optional[str]) -> Optional[str]:
    """若 icon_url 为 MinIO object_key，转为预签名 URL"""
    if not icon_url:
        return icon_url
    if str(icon_url).startswith("http"):
        return icon_url
    return minio_storage.get_distribution_presigned_url(icon_url) or icon_url


def _check_distribution_access(db: Session, dist_id: int, user: User) -> ClientDistribution:
    dist = db.query(ClientDistribution).filter(
        ClientDistribution.id == dist_id,
        ClientDistribution.is_deleted == 0
    ).first()
    if not dist:
        raise HTTPException(status_code=404, detail="分发不存在")
    if user.role != "SUPER_ADMIN" and dist.owner_id != user.id:
        raise HTTPException(status_code=403, detail="无权限操作此分发")
    return dist


@router.get("/", response_model=ClientDistributionList)
def list_distributions(
    skip: int = 0,
    limit: int = 10,
    search: Optional[str] = None,
    db: Session = Depends(deps.get_db),
    current_user: User = Depends(deps.get_current_active_user),
):
    """获取分发列表。超级管理员看全部，开发者只看自己的。"""
    query = db.query(ClientDistribution).filter(ClientDistribution.is_deleted == 0)
    if current_user.role != "SUPER_ADMIN":
        query = query.filter(ClientDistribution.owner_id == current_user.id)
    if search:
        query = query.filter(
            or_(
                ClientDistribution.name.ilike(f"%{search}%"),
            )
        )
    total = query.count()
    rows = query.order_by(desc(ClientDistribution.id)).offset(skip).limit(limit).all()
    items = [
        ClientDistributionResponse(
            id=d.id,
            name=d.name,
            description=d.description,
            icon_url=_resolve_icon_url(d.icon_url),
            icon_object_key=d.icon_url if (d.icon_url and not str(d.icon_url).startswith("http")) else None,
            owner_id=d.owner_id,
            created_at=d.created_at,
            updated_at=d.updated_at,
            share_id=encode_distribution_share_id(d.id, d.created_at),
        )
        for d in rows
    ]
    return ClientDistributionList(total=total, items=items)


@router.post("/", response_model=ClientDistributionResponse)
def create_distribution(
    data: ClientDistributionCreate,
    db: Session = Depends(deps.get_db),
    current_user: User = Depends(deps.get_current_active_user),
):
    """创建分发。仅开发者或超级管理员可创建。"""
    if current_user.role not in ("SUPER_ADMIN", "DEVELOPER"):
        raise HTTPException(status_code=403, detail="无权限创建分发")
    dist = ClientDistribution(
        name=data.name,
        description=data.description,
        icon_url=data.icon_url,
        owner_id=current_user.id,
    )
    db.add(dist)
    db.commit()
    db.refresh(dist)
    return ClientDistributionResponse(
        id=dist.id,
        name=dist.name,
        description=dist.description,
        icon_url=_resolve_icon_url(dist.icon_url),
        icon_object_key=dist.icon_url if (dist.icon_url and not str(dist.icon_url).startswith("http")) else None,
        owner_id=dist.owner_id,
        created_at=dist.created_at,
        updated_at=dist.updated_at,
        share_id=encode_distribution_share_id(dist.id, dist.created_at),
    )


@router.post("/icon/upload")
async def upload_icon(
    file: UploadFile = File(...),
    distribution_id: int = Form(0),
    db: Session = Depends(deps.get_db),
    current_user: User = Depends(deps.get_current_active_user),
):
    """上传分发图标。distribution_id=0 表示创建时的临时图标，>0 表示编辑已有分发。返回 object_key。"""
    if current_user.role not in ("SUPER_ADMIN", "DEVELOPER"):
        raise HTTPException(status_code=403, detail="无权限")
    if distribution_id > 0:
        _check_distribution_access(db, distribution_id, current_user)
    content = await file.read()
    if len(content) > MAX_ICON_SIZE:
        raise HTTPException(status_code=400, detail="图标大小超过 2MB 限制")
    ct = file.content_type or "image/png"
    if ct not in ALLOWED_ICON_TYPES:
        raise HTTPException(status_code=400, detail="仅支持 PNG、JPG、GIF、WebP 格式")
    try:
        object_key = minio_storage.upload_distribution_icon(
            file_data=content,
            filename=file.filename or "icon.png",
            content_type=ct,
            distribution_id=distribution_id,
            user_id=current_user.id,
        )
        return {"object_key": object_key}
    except Exception as e:
        logger.error(f"Icon upload failed: {e}")
        raise HTTPException(status_code=500, detail="图标上传失败")


@router.get("/{dist_id}", response_model=ClientDistributionResponse)
def get_distribution(
    dist_id: int,
    db: Session = Depends(deps.get_db),
    current_user: User = Depends(deps.get_current_active_user),
):
    """获取分发详情"""
    dist = _check_distribution_access(db, dist_id, current_user)
    return ClientDistributionResponse(
        id=dist.id,
        name=dist.name,
        description=dist.description,
        icon_url=_resolve_icon_url(dist.icon_url),
        icon_object_key=dist.icon_url if (dist.icon_url and not str(dist.icon_url).startswith("http")) else None,
        owner_id=dist.owner_id,
        created_at=dist.created_at,
        updated_at=dist.updated_at,
        share_id=encode_distribution_share_id(dist.id, dist.created_at),
    )


@router.put("/{dist_id}", response_model=ClientDistributionResponse)
def update_distribution(
    dist_id: int,
    data: ClientDistributionUpdate,
    db: Session = Depends(deps.get_db),
    current_user: User = Depends(deps.get_current_active_user),
):
    """更新分发"""
    dist = _check_distribution_access(db, dist_id, current_user)
    if data.name is not None:
        dist.name = data.name
    if data.description is not None:
        dist.description = data.description
    if data.icon_url is not None:
        dist.icon_url = data.icon_url
    db.commit()
    db.refresh(dist)
    return ClientDistributionResponse(
        id=dist.id,
        name=dist.name,
        description=dist.description,
        icon_url=_resolve_icon_url(dist.icon_url),
        icon_object_key=dist.icon_url if (dist.icon_url and not str(dist.icon_url).startswith("http")) else None,
        owner_id=dist.owner_id,
        created_at=dist.created_at,
        updated_at=dist.updated_at,
        share_id=encode_distribution_share_id(dist.id, dist.created_at),
    )


@router.delete("/{dist_id}")
def delete_distribution(
    dist_id: int,
    db: Session = Depends(deps.get_db),
    current_user: User = Depends(deps.get_current_active_user),
):
    """软删除分发"""
    dist = _check_distribution_access(db, dist_id, current_user)
    dist.is_deleted = 1
    db.commit()
    return {"message": "已删除"}


# ========== Versions ==========

@router.get("/{dist_id}/versions", response_model=ClientVersionList)
def list_versions(
    dist_id: int,
    skip: int = 0,
    limit: int = 20,
    platform: Optional[str] = None,
    search: Optional[str] = None,
    db: Session = Depends(deps.get_db),
    current_user: User = Depends(deps.get_current_active_user),
):
    """获取分发下的版本列表（按语义版本号从新到旧排序）。支持按平台分类筛选与关键词聚合检索。"""
    _check_distribution_access(db, dist_id, current_user)
    query = db.query(ClientVersion).filter(ClientVersion.distribution_id == dist_id)
    if platform and platform.strip():
        query = query.filter(ClientVersion.platform == platform.strip().lower())
    if search and search.strip():
        term = f"%{search.strip()}%"
        query = query.filter(
            or_(
                ClientVersion.version_code.ilike(term),
                ClientVersion.version_name.ilike(term),
                ClientVersion.release_notes.ilike(term),
            )
        )
    all_versions = query.all()
    all_versions.sort(key=lambda v: version_sort_key(v.version_code), reverse=True)
    total = len(all_versions)
    items = all_versions[skip : skip + limit]
    return ClientVersionList(total=total, items=items)


@router.post("/{dist_id}/versions", response_model=ClientVersionResponse)
async def create_version(
    dist_id: int,
    file: UploadFile = File(...),
    version_code: str = Form(...),
    version_name: Optional[str] = Form(None),
    release_notes: Optional[str] = Form(None),
    platform: Optional[str] = Form(None),
    db: Session = Depends(deps.get_db),
    current_user: User = Depends(deps.get_current_active_user),
):
    """创建新版本（上传安装包）。平台必填；版本号不能低于该平台当前最新版本。"""
    dist = _check_distribution_access(db, dist_id, current_user)

    # 平台必填并规范化
    if not platform or not str(platform).strip():
        raise HTTPException(status_code=400, detail="请选择平台")
    platform_lower = str(platform).strip().lower()
    allowed = {p.value for p in DistributionPlatform}
    if platform_lower not in allowed:
        raise HTTPException(
            status_code=400,
            detail=f"平台必须是以下之一: {', '.join(sorted(allowed))}",
        )
    platform = platform_lower

    # 校验版本号格式
    version_code = (version_code or "").strip()
    try:
        validate_version_code(version_code)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))

    # 该平台下版本号不能低于已有最新版本
    versions_same_platform = (
        db.query(ClientVersion)
        .filter(
            ClientVersion.distribution_id == dist_id,
            ClientVersion.platform == platform,
        )
        .all()
    )
    if versions_same_platform:
        latest_same_platform = max(
            versions_same_platform,
            key=lambda v: version_sort_key(v.version_code),
        )
        if version_sort_key(version_code) < version_sort_key(latest_same_platform.version_code):
            raise HTTPException(
                status_code=400,
                detail=f"该平台当前最新版本为 {latest_same_platform.version_code}，新版本号不能低于此版本",
            )

    # 检查版本号唯一（同分发内版本号不可重复）
    existing = db.query(ClientVersion).filter(
        ClientVersion.distribution_id == dist_id,
        ClientVersion.version_code == version_code
    ).first()
    if existing:
        raise HTTPException(status_code=400, detail=f"版本号 {version_code} 已存在")

    content = await file.read()
    if len(content) > MAX_PACKAGE_SIZE:
        raise HTTPException(status_code=400, detail="文件大小超过 500MB 限制")

    content_type = file.content_type or "application/octet-stream"
    if content_type not in ALLOWED_PACKAGE_TYPES and not content_type.startswith("application/"):
        pass  # 放宽：允许 application/*

    try:
        object_key = minio_storage.upload_distribution_file(
            file_data=content,
            filename=file.filename or "package.bin",
            content_type=content_type,
            distribution_id=dist_id,
        )
    except Exception as e:
        logger.error(f"Upload failed: {e}")
        raise HTTPException(status_code=500, detail="文件上传失败")

    version = ClientVersion(
        distribution_id=dist_id,
        version_code=version_code,
        version_name=version_name or version_code,
        release_notes=release_notes,
        object_key=object_key,
        file_size=len(content),
        platform=platform,
    )
    db.add(version)
    db.commit()
    db.refresh(version)
    return version


@router.put("/versions/{version_id}", response_model=ClientVersionResponse)
def update_version(
    version_id: int,
    data: ClientVersionUpdate,
    db: Session = Depends(deps.get_db),
    current_user: User = Depends(deps.get_current_active_user),
):
    """更新版本信息（不含文件）"""
    version = db.query(ClientVersion).filter(ClientVersion.id == version_id).first()
    if not version:
        raise HTTPException(status_code=404, detail="版本不存在")
    _check_distribution_access(db, version.distribution_id, current_user)
    if data.version_name is not None:
        version.version_name = data.version_name
    if data.release_notes is not None:
        version.release_notes = data.release_notes
    if data.platform is not None:
        version.platform = data.platform
    db.commit()
    db.refresh(version)
    return version


@router.delete("/versions/{version_id}")
def delete_version(
    version_id: int,
    db: Session = Depends(deps.get_db),
    current_user: User = Depends(deps.get_current_active_user),
):
    """删除版本"""
    version = db.query(ClientVersion).filter(ClientVersion.id == version_id).first()
    if not version:
        raise HTTPException(status_code=404, detail="版本不存在")
    _check_distribution_access(db, version.distribution_id, current_user)
    db.delete(version)
    db.commit()
    return {"message": "已删除"}


# ========== 自建更新文件（update 桶，公开读）==========

@router.get("/{dist_id}/update-files")
def list_update_files(
    dist_id: int,
    platform: str,
    db: Session = Depends(deps.get_db),
    current_user: User = Depends(deps.get_current_active_user),
):
    """列出该分发、该平台下 update/ 目录中的文件（MinIO 路径：创建时间戳/平台/update/...）"""
    dist = _check_distribution_access(db, dist_id, current_user)
    if not platform or platform.strip().lower() not in {p.value for p in DistributionPlatform}:
        raise HTTPException(status_code=400, detail="请选择有效平台")
    platform = platform.strip().lower()
    ts = int(dist.created_at.timestamp())
    files = minio_storage.list_update_files(ts, platform)
    base_url = minio_storage._update_bucket_public_base_url()
    for f in files:
        f["url"] = minio_storage.get_update_file_public_url(f["key"])
    return {"base_url": f"{base_url}{ts}/{platform}/update/", "files": files}


@router.post("/{dist_id}/update-files")
async def upload_update_file(
    dist_id: int,
    platform: str = Form(...),
    file: UploadFile = File(...),
    db: Session = Depends(deps.get_db),
    current_user: User = Depends(deps.get_current_active_user),
):
    """上传文件到该分发、该平台的 update/ 目录，透传不改名"""
    dist = _check_distribution_access(db, dist_id, current_user)
    if platform.strip().lower() not in {p.value for p in DistributionPlatform}:
        raise HTTPException(status_code=400, detail="请选择有效平台")
    platform = platform.strip().lower()
    filename = file.filename or "file.bin"
    content = await file.read()
    ts = int(dist.created_at.timestamp())
    object_key = minio_storage.upload_update_file(
        created_at_timestamp=ts,
        platform=platform,
        file_data=content,
        filename=filename,
        content_type=file.content_type or "application/octet-stream",
    )
    url = minio_storage.get_update_file_public_url(object_key)
    return {"object_key": object_key, "url": url}


@router.delete("/{dist_id}/update-files")
def delete_update_file(
    dist_id: int,
    platform: str,
    key: str,
    db: Session = Depends(deps.get_db),
    current_user: User = Depends(deps.get_current_active_user),
):
    """删除 update/ 目录下的文件，key 为 list 接口返回的完整 object_key"""
    dist = _check_distribution_access(db, dist_id, current_user)
    if not platform or not key:
        raise HTTPException(status_code=400, detail="缺少 platform 或 key")
    platform = platform.strip().lower()
    ts = int(dist.created_at.timestamp())
    prefix = f"{ts}/{platform}/update/"
    if not key.startswith(prefix):
        raise HTTPException(status_code=400, detail="key 不属当前分发/平台")
    minio_storage.delete_update_file(key)
    return {"message": "已删除"}
