"""Сетевые хранилища: CRUD шард-путей + история заполнения.

Пути опрашивает shares_probe (lifespan, каждые GHARD_SHARE_INTERVAL сек);
точки лежат в share_samples. Статус: online (свежая ok=1 точка),
offline (свежая ok=0), pending (точек ещё нет).
"""

from datetime import datetime, timezone

from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel, Field

from app.config import get_settings
from app.db import get_db
from app.security import require_admin
from app.shares_probe import sample_once

router = APIRouter(prefix="/api/v1", dependencies=[Depends(require_admin)])


def _now_iso() -> str:
    return datetime.now(timezone.utc).isoformat()


class ShareCreate(BaseModel):
    name: str = Field(min_length=1, max_length=100)
    path: str = Field(min_length=1, max_length=500)


class ShareUpdate(BaseModel):
    name: str | None = None
    path: str | None = None


@router.get("/shares")
async def list_shares() -> list[dict]:
    """Все шард-ы + последняя точка заполнения."""
    db = get_db()
    cursor = await db.execute(
        """SELECT sh.*, p.ts AS p_ts, p.total AS p_total, p.used AS p_used,
                  p.ok AS p_ok
           FROM shares sh
           LEFT JOIN share_samples p ON p.id = (
               SELECT MAX(id) FROM share_samples WHERE share_id = sh.id
           )
           ORDER BY sh.name"""
    )
    rows = await cursor.fetchall()
    stale_after = max(get_settings().share_interval * 3, 180)
    result = []
    for row in rows:
        if row["p_ts"] is None:
            status = "pending"
        else:
            age = (datetime.now(timezone.utc) - datetime.fromisoformat(row["p_ts"])).total_seconds()
            if age > stale_after:
                status = "pending"  # пробер молчит — нет свежих данных
            else:
                status = "online" if row["p_ok"] else "offline"
        result.append(
            {
                "id": row["id"],
                "name": row["name"],
                "path": row["path"],
                "created_at": row["created_at"],
                "status": status,
                "last_sample": row["p_ts"],
                "total": row["p_total"] if row["p_ok"] else None,
                "used": row["p_used"] if row["p_ok"] else None,
            }
        )
    return result


@router.post("/shares", status_code=201)
async def create_share(body: ShareCreate) -> dict:
    db = get_db()
    cursor = await db.execute(
        "INSERT INTO shares (name, path, created_at) VALUES (?, ?, ?)",
        (body.name, body.path, _now_iso()),
    )
    await db.commit()
    # первый замер сразу — не ждём ближайшей волны пробера
    try:
        await sample_once()
    except Exception as exc:  # замер доберёт фоновый цикл
        print(f"shares probe error (on create): {exc}", flush=True)
    return {"id": cursor.lastrowid, "name": body.name, "path": body.path}


@router.patch("/shares/{share_id}")
async def update_share(share_id: int, body: ShareUpdate) -> dict:
    if body.name is None and body.path is None:
        raise HTTPException(status_code=422, detail="nothing to update")
    db = get_db()
    if body.name is not None:
        await db.execute("UPDATE shares SET name = ? WHERE id = ?", (body.name, share_id))
    if body.path is not None:
        await db.execute("UPDATE shares SET path = ? WHERE id = ?", (body.path, share_id))
    cursor = await db.execute("SELECT changes()")
    (changed,) = await cursor.fetchone()
    if not changed:
        raise HTTPException(status_code=404, detail="share not found")
    await db.commit()
    if body.path is not None:  # путь сменился — замеряем не дожидаясь волны
        try:
            await sample_once()
        except Exception as exc:
            print(f"shares probe error (on update): {exc}", flush=True)
    cursor = await db.execute("SELECT id, name, path, created_at FROM shares WHERE id = ?", (share_id,))
    return dict(await cursor.fetchone())


@router.delete("/shares/{share_id}", status_code=204)
async def delete_share(share_id: int) -> None:
    db = get_db()
    cursor = await db.execute("DELETE FROM shares WHERE id = ?", (share_id,))
    await db.commit()
    if cursor.rowcount == 0:
        raise HTTPException(status_code=404, detail="share not found")


@router.get("/shares/{share_id}/samples")
async def share_samples(
    share_id: int,
    since: datetime | None = Query(default=None),
    limit: int = Query(default=500, ge=1, le=5000),
) -> list[dict]:
    """История заполнения (для графика). По возрастанию ts."""
    db = get_db()
    conditions = ["share_id = ?"]
    params: list = [share_id]
    if since is not None:
        conditions.append("ts >= ?")
        params.append(since.astimezone(timezone.utc).isoformat())
    params.append(limit)
    cursor = await db.execute(
        f"SELECT ts, total, used, ok FROM share_samples WHERE {' AND '.join(conditions)} ORDER BY ts DESC LIMIT ?",
        params,
    )
    rows = await cursor.fetchall()
    return [
        {
            "ts": row["ts"],
            "total": row["total"],
            "used": row["used"],
            "ok": bool(row["ok"]),
        }
        for row in reversed(rows)
    ]