import { apiFetch, apiJSON } from "./api";

// 登录会话 / 设备的网络封装。全部走 apiFetch（带 Bearer + 401 自动续期）；
// refresh_token 永不进 JS——吊销由后端令短 TTL 的 access_token 随之失效完成。

// ---- /api/me：本会话权限级别 ------------------------------------------------

export type SessionScopeValue = "full" | "guest";

interface MeRaw
{
    // /api/me 现回传本会话的 scope（full=完整登录、guest=受限访客）。旧后端 / 异常
    // 缺字段时按 "full" 解析（只多展示入口，后端仍 403 兜底）。
    scope?: string;
}

// fetchMeScope 拉取 /api/me 并解析本会话权限级别。失败 / 缺字段回退 "full"。
export async function fetchMeScope(): Promise<SessionScopeValue>
{
    try
    {
        const data = await apiJSON<MeRaw>("/api/me");
        return data.scope === "guest" ? "guest" : "full";
    }
    catch
    {
        return "full";
    }
}

// ---- 登录会话列表 / 吊销 / step-up ------------------------------------------

// 会话「种类」：oidc / self / guest 为网页 / 扫码会话；macos / windows / linux / ios
// 为原生客户端（桌面 / 移动）——它们用 IdP 令牌、不入 web_sessions，由设备登记表呈现。
export type SessionKind =
    | "oidc" | "self" | "guest"
    | "macos" | "windows" | "linux" | "ios";

const KNOWN_KINDS: readonly SessionKind[] = [
    "oidc", "self", "guest", "macos", "windows", "linux", "ios",
];

export interface AuthSession
{
    id: string;
    deviceName: string;
    kind: SessionKind;
    scope: SessionScopeValue;
    // current=true：这条就是本机当前会话（登出它等价于退出登录）。
    current: boolean;
    // online=true：该设备当前在线（有活跃 SSE 连接）。用于列表排序与在线指示。
    online: boolean;
    // native=true：原生客户端（无 web_session），登出走设备登记端点而非会话端点。
    native: boolean;
    createdAt: number;
    lastUsedAt: number;
    expiresAt: number;
}

interface AuthSessionRaw
{
    id: string;
    device_name?: string;
    kind?: string;
    scope?: string;
    current?: boolean;
    online?: boolean;
    native?: boolean;
    created_at?: number;
    last_used_at?: number;
    expires_at?: number;
}

interface SessionsResp
{
    sessions: AuthSessionRaw[];
}

function normalizeKind(kind: string | undefined): SessionKind
{
    return KNOWN_KINDS.includes(kind as SessionKind) ? (kind as SessionKind) : "oidc";
}

// listSessions 拉取当前账号下的全部登录会话（含本机），每条带 online（当前是否在线）。
// 受限访客无权管理登录会话、后端会 403，故调用方（SessionsPanel）在 guest 时根本不发
// 此请求、直接展示克制说明，不会走到这里。
export async function listSessions(): Promise<AuthSession[]>
{
    const data = await apiJSON<SessionsResp>("/api/auth/sessions");
    const rows = Array.isArray(data.sessions) ? data.sessions : [];
    return rows.map((r) => (
    {
        id: r.id,
        deviceName: r.device_name ?? "",
        kind: normalizeKind(r.kind),
        scope: r.scope === "guest" ? "guest" : "full",
        current: r.current === true,
        online: r.online === true,
        native: r.native === true,
        createdAt: r.created_at ?? 0,
        lastUsedAt: r.last_used_at ?? 0,
        expiresAt: r.expires_at ?? 0,
    }));
}

// StepUpRequiredError：DELETE /api/auth/sessions/{id} 因再认证缺失 / 过期被后端拒
// （403 {error:"step_up_required"}）。调用方据此发起一次 step-up 再认证而非当成
// 普通错误。
export class StepUpRequiredError extends Error
{
    constructor()
    {
        super("step_up_required");
        this.name = "StepUpRequiredError";
    }
}

// revokeSession 真正吊销一条登录会话。网页 / 扫码会话走 /auth/sessions/{id}（删会话
// 行 + 连带删设备）；原生客户端（id 形如 "device:设备名"）无 web_session，改走
// /devices/{name} 按设备名注销。两者均：成功 204；后端要求再认证时抛
// StepUpRequiredError（403 step_up_required），其余非 2xx 抛普通错误。
const NATIVE_ID_PREFIX = "device:";

export async function revokeSession(id: string): Promise<void>
{
    const path = id.startsWith(NATIVE_ID_PREFIX)
        ? `/api/devices/${encodeURIComponent(id.slice(NATIVE_ID_PREFIX.length))}`
        : `/api/auth/sessions/${encodeURIComponent(id)}`;
    const r = await apiFetch(path, { method: "DELETE" });
    if (!r.ok)
    {
        const text = await r.text().catch(() => r.statusText);
        if (r.status === 403 && /step_up_required/.test(text))
        {
            throw new StepUpRequiredError();
        }
        throw new Error(`revoke session ${r.status}: ${text}`);
    }
}

// postStepUp 把一次新鲜 prompt=login 再认证拿到的 {code, verifier} 交给后端就地
// 核验，成功后服务端在本会话记下 stepped_up_at（5 分钟窗口内复用，不再反复要求）。
// 成功 204；step-up 未开后端亦 204（视作通过）；失败 403。
export async function postStepUp(code: string, verifier: string): Promise<void>
{
    const r = await apiFetch("/api/auth/stepup", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ code, verifier }),
    });
    if (!r.ok)
    {
        const text = await r.text().catch(() => r.statusText);
        throw new Error(`step-up ${r.status}: ${text}`);
    }
}
