import { cookieNames } from '../auth/cookies.js';
import { requireAllowedOrigin } from './authRoutes.js';
export async function authenticateTerminalRequest(authService, request, reply) {
    const auth = authService;
    const methodNames = ['requireSession', 'requireAuth', 'authenticateRequest', 'validateRequest', 'isAuthenticated', 'validateSession'];
    for (const methodName of methodNames) {
        const method = auth[methodName];
        if (typeof method !== 'function') {
            continue;
        }
        if (method.length > 2) {
            continue;
        }
        const result = await method.call(authService, request, reply);
        const authContext = normalizeAuthResult(result, reply);
        if (!authContext) {
            await logAuthFailure(authService, request, methodName);
        }
        return authContext;
    }
    if (reply && !reply.sent) {
        await reply.code(401).send({ error: 'unauthorized' });
    }
    await logAuthFailure(authService, request, 'none');
    return null;
}
export async function registerTerminalRoutes(app, config, services) {
    app.get('/api/terminals', async (request, reply) => {
        const auth = await authenticateTerminalRequest(services.authService, request, reply);
        if (!auth) {
            return reply;
        }
        return { terminals: services.terminalManager.listTerminals() };
    });
    app.get('/api/terminals/:id/history', async (request, reply) => {
        const auth = await authenticateTerminalRequest(services.authService, request, reply);
        if (!auth) {
            return reply;
        }
        const beforeLine = parseOptionalInt(request.query.beforeLine, 'beforeLine', reply);
        if (beforeLine === null) {
            return reply;
        }
        const limit = parseOptionalInt(request.query.limit, 'limit', reply);
        if (limit === null) {
            return reply;
        }
        const history = services.terminalManager.getTerminalHistory(request.params.id, {
            ...(beforeLine !== undefined ? { beforeLine } : {}),
            ...(limit !== undefined ? { limit } : {})
        });
        if (!history) {
            return reply.code(404).send({ error: 'terminal_history_unavailable' });
        }
        return history;
    });
    app.get('/api/terminals/:id/transcript-history', async (request, reply) => {
        const auth = await authenticateTerminalRequest(services.authService, request, reply);
        if (!auth) {
            return reply;
        }
        const limit = parseOptionalInt(request.query.limit, 'limit', reply);
        if (limit === null) {
            return reply;
        }
        const maxLines = parseOptionalInt(request.query.maxLines, 'maxLines', reply);
        if (maxLines === null) {
            return reply;
        }
        const includeTools = parseOptionalBool(request.query.includeTools, 'includeTools', reply);
        if (includeTools === null) {
            return reply;
        }
        const history = services.terminalManager.getTerminalTranscriptHistory(request.params.id, {
            ...(typeof request.query.cursor === 'string' && request.query.cursor ? { cursor: request.query.cursor } : {}),
            ...(limit !== undefined ? { limit } : {}),
            ...(maxLines !== undefined ? { maxLines } : {}),
            ...(includeTools !== undefined ? { includeTools } : {})
        });
        if (!history) {
            if (!services.terminalManager.getTerminal(request.params.id)) {
                return reply.code(404).send({ error: 'terminal_not_found' });
            }
            return {
                terminalId: request.params.id,
                source: null,
                cursor: null,
                hasMore: false,
                output: []
            };
        }
        return history;
    });
    app.get('/api/terminals/:id/transcript-source', async (request, reply) => {
        const auth = await authenticateTerminalRequest(services.authService, request, reply);
        if (!auth) {
            return reply;
        }
        const source = services.terminalManager.getTerminalTranscriptSource(request.params.id);
        if (!source) {
            if (!services.terminalManager.getTerminal(request.params.id)) {
                return reply.code(404).send({ error: 'terminal_not_found' });
            }
            return { terminalId: request.params.id, source: null };
        }
        return source;
    });
    app.post('/api/terminals', { preHandler: (request, reply) => requireAllowedOrigin(config, request, reply) }, async (request, reply) => {
        const auth = await authenticateTerminalRequest(services.authService, request, reply);
        if (!auth) {
            return reply;
        }
        const body = (request.body ?? {});
        const createOptions = {
            ...(body.parentTerminalId !== undefined ? { parentTerminalId: body.parentTerminalId } : {}),
            ...(body.cols !== undefined ? { cols: body.cols } : {}),
            ...(body.rows !== undefined ? { rows: body.rows } : {})
        };
        const terminal = await services.terminalManager.createTerminal(createOptions);
        return reply.code(201).send({ terminal });
    });
    app.delete('/api/terminals/:id', { preHandler: (request, reply) => requireAllowedOrigin(config, request, reply) }, async (request, reply) => {
        const auth = await authenticateTerminalRequest(services.authService, request, reply);
        if (!auth) {
            return reply;
        }
        if (!services.terminalManager.closeTerminal(request.params.id)) {
            return reply.code(404).send({ error: 'terminal_not_found' });
        }
        return reply.code(204).send();
    });
    // Update an existing terminal's title. We use PATCH so the request body
    // only carries the fields the client wants to change — currently just
    // `title`, but the shape lets us add more fields (cwd hint, color, etc.)
    // later without a new endpoint.
    app.patch('/api/terminals/:id', { preHandler: (request, reply) => requireAllowedOrigin(config, request, reply) }, async (request, reply) => {
        const auth = await authenticateTerminalRequest(services.authService, request, reply);
        if (!auth) {
            return reply;
        }
        const titleField = (request.body ?? {}).title;
        if (typeof titleField !== 'string' || !titleField.trim()) {
            return reply.code(400).send({ error: 'title_required' });
        }
        const summary = services.terminalManager.renameTerminal(request.params.id, titleField);
        if (!summary) {
            return reply.code(404).send({ error: 'terminal_not_found' });
        }
        return { terminal: summary };
    });
    app.delete('/api/terminals', { preHandler: (request, reply) => requireAllowedOrigin(config, request, reply) }, async (request, reply) => {
        const auth = await authenticateTerminalRequest(services.authService, request, reply);
        if (!auth) {
            return reply;
        }
        services.terminalManager.closeAll();
        return reply.code(204).send();
    });
}
function normalizeAuthResult(result, reply) {
    if (result === true) {
        return {};
    }
    if (typeof result === 'string') {
        return { sessionId: result };
    }
    if (result && typeof result === 'object') {
        if (result.authenticated === false) {
            sendUnauthorized(reply);
            return null;
        }
        return typeof result.sessionId === 'string' ? { sessionId: result.sessionId } : {};
    }
    sendUnauthorized(reply);
    return null;
}
function sendUnauthorized(reply) {
    if (reply && !reply.sent) {
        void reply.code(401).send({ error: 'unauthorized' });
    }
}
async function logAuthFailure(authService, request, guard) {
    const requestCookies = (request.cookies ?? {});
    const diagnostic = typeof authService.diagnoseSession === 'function'
        ? await authService.diagnoseSession(requestCookies[cookieNames.session], request.ip)
        : {
            sessionCookiePresent: Boolean(requestCookies[cookieNames.session])
        };
    request.log.warn({
        event: 'auth.required_failed',
        guard,
        method: request.method,
        path: request.url.split('?', 1)[0],
        deviceCookiePresent: Boolean(requestCookies[cookieNames.device]),
        ...diagnostic
    }, 'auth required failed');
}
function parseOptionalInt(value, name, reply) {
    if (value === undefined || value === '') {
        return undefined;
    }
    const parsed = Number(value);
    if (!Number.isInteger(parsed)) {
        reply.code(400).send({ error: `invalid_${name}` });
        return null;
    }
    return parsed;
}
function parseOptionalBool(value, name, reply) {
    if (value === undefined || value === '') {
        return undefined;
    }
    if (value === 'true') {
        return true;
    }
    if (value === 'false') {
        return false;
    }
    reply.code(400).send({ error: `invalid_${name}` });
    return null;
}
//# sourceMappingURL=terminalRoutes.js.map