import { SignJWT } from 'jose';
import QRCode from 'qrcode';
import { env } from '../env.js';
import { bus } from '../ws-bus.js';
import { logger } from '../logger.js';
import { ApproveBodySchema, StartBodySchema, TokenBodySchema } from './types.js';
import { MemoryStore, RedisStore } from './store.js';
import { redis } from '../redis.js';
const ACCESS_TOKEN_TTL = 60 * 60;
const POLL_INTERVAL = 5;
const jwtSecretBytes = new TextEncoder().encode(env.JWT_SECRET);
async function signSessionJwt(userId, deviceCode) {
    return new SignJWT({ sub: userId, dc: deviceCode })
        .setProtectedHeader({ alg: 'HS256' })
        .setIssuedAt()
        .setExpirationTime(`${ACCESS_TOKEN_TTL}s`)
        .sign(jwtSecretBytes);
}
export function buildDeviceGrantPlugin(options = {}) {
    const store = options.store ?? makeDefaultStore();
    return async function deviceGrantRoutes(app) {
        app.post('/auth/device', async (req, reply) => {
            const body = StartBodySchema.safeParse(req.body ?? {});
            if (!body.success) {
                reply.code(400);
                return { error: 'invalid_request' };
            }
            const rec = await store.create(body.data.sessionId ?? null);
            const verificationUri = `${env.CONTROL_PLANE_PUBLIC_URL}/auth/device/verify`;
            const verificationUriComplete = `${verificationUri}?user_code=${rec.user_code}`;
            const qr = await QRCode.toDataURL(verificationUriComplete, { width: 320, margin: 1 });
            const out = {
                device_code: rec.device_code,
                user_code: rec.user_code,
                verification_uri: verificationUri,
                verification_uri_complete: verificationUriComplete,
                expires_in: rec.expires_at - rec.created_at,
                interval: POLL_INTERVAL,
                qr_data_url: qr,
            };
            return out;
        });
        app.get('/auth/device/verify', async (req, reply) => {
            const code = req.query?.user_code;
            if (!code) {
                reply.code(400);
                return { error: 'missing_user_code' };
            }
            reply.type('text/html');
            return `<!doctype html><html lang="pt-BR"><head><meta charset="utf-8"><title>Vulcan Music - Aprovar</title>
<style>body{font-family:system-ui;background:#0a0a0a;color:#f3c97a;display:grid;place-items:center;height:100vh;margin:0}
form{background:#161616;padding:32px;border-radius:12px;display:grid;gap:12px;min-width:280px}
input,button{padding:10px;border-radius:8px;border:1px solid #333;background:#0a0a0a;color:#fff}
button{background:#f3c97a;color:#0a0a0a;font-weight:600;cursor:pointer}</style></head>
<body><form method="POST" action="/auth/device/approve">
<h2>Aprovar tela</h2><p>Codigo: <strong>${code}</strong></p>
<input type="hidden" name="user_code" value="${code}">
<input name="user_id" placeholder="seu email ou id" required>
<button type="submit">Aprovar</button></form></body></html>`;
        });
        app.post('/auth/device/approve', async (req, reply) => {
            const body = ApproveBodySchema.safeParse(req.body ?? {});
            if (!body.success) {
                reply.code(400);
                return { error: 'invalid_request' };
            }
            const rec = await store.approveByUserCode(body.data.user_code, body.data.user_id);
            if (!rec) {
                reply.code(404);
                return { error: 'unknown_user_code' };
            }
            if (rec.session_id) {
                const token = await signSessionJwt(body.data.user_id, rec.device_code);
                bus.send(rec.session_id, { type: 'auth_granted', access_token: token, expires_in: ACCESS_TOKEN_TTL });
            }
            return { ok: true };
        });
        app.post('/auth/device/token', async (req, reply) => {
            const body = TokenBodySchema.safeParse(req.body ?? {});
            if (!body.success) {
                reply.code(400);
                return { error: 'invalid_request' };
            }
            const rec = await store.poll(body.data.device_code);
            if (!rec) {
                reply.code(404);
                return { error: 'unknown_device_code' };
            }
            if (rec.status === 'denied') {
                reply.code(400);
                return { error: 'access_denied' };
            }
            if (rec.status !== 'approved' || !rec.user_id) {
                reply.code(400);
                return { error: 'authorization_pending' };
            }
            const token = await signSessionJwt(rec.user_id, rec.device_code);
            const out = { access_token: token, token_type: 'Bearer', expires_in: ACCESS_TOKEN_TTL };
            return out;
        });
    };
}
function makeDefaultStore() {
    try {
        return new RedisStore(redis());
    }
    catch (err) {
        logger.warn({ err }, 'falling back to memory store for device grant');
        return new MemoryStore();
    }
}
//# sourceMappingURL=routes.js.map