import express from 'express';
import { createProject, updateProjectDisplayName } from '../../modules/projects/services/project-management.service.js';
import { startCloneProject } from '../../modules/projects/services/project-clone.service.js';
import { getProjectTaskMaster } from '../../modules/projects/services/projects-has-taskmaster.service.js';
import { AppError, asyncHandler, createApiSuccessResponse } from '../../shared/utils.js';
import { getArchivedProjectsWithSessions, getProjectSessionsPage, getProjectsWithSessions } from '../../modules/projects/services/projects-with-sessions-fetch.service.js';
import { deleteOrArchiveProject, restoreArchivedProject } from '../../modules/projects/services/project-delete.service.js';
import { applyLegacyStarredProjectIds, toggleProjectStar } from '../../modules/projects/services/project-star.service.js';
const router = express.Router();
function getAuthenticatedUserId(req) {
    const authenticatedUser = req.user;
    const rawId = authenticatedUser?.id;
    if (rawId === undefined || rawId === null) {
        throw new AppError('Authenticated user is required', {
            code: 'AUTHENTICATION_REQUIRED',
            statusCode: 401,
        });
    }
    const numericId = typeof rawId === 'number' ? rawId : Number.parseInt(String(rawId), 10);
    if (Number.isNaN(numericId)) {
        throw new AppError('Authenticated user is required', {
            code: 'AUTHENTICATION_REQUIRED',
            statusCode: 401,
        });
    }
    return numericId;
}
function getAuthenticatedUsername(req) {
    const authenticatedUser = req.user;
    const username = authenticatedUser?.username;
    if (typeof username !== 'string' || username.length === 0) {
        throw new AppError('Authenticated user is required', {
            code: 'AUTHENTICATION_REQUIRED',
            statusCode: 401,
        });
    }
    return username;
}
function readQueryStringValue(value) {
    if (typeof value === 'string') {
        return value;
    }
    if (Array.isArray(value) && typeof value[0] === 'string') {
        return value[0];
    }
    return '';
}
function readOptionalNumericQueryValue(value) {
    const rawValue = readQueryStringValue(value).trim();
    if (!rawValue) {
        return null;
    }
    const parsedValue = Number.parseInt(rawValue, 10);
    return Number.isNaN(parsedValue) ? null : parsedValue;
}
function parseNonNegativeIntQuery(value, name, fallback) {
    const rawValue = readQueryStringValue(value).trim();
    if (!rawValue) {
        return fallback;
    }
    const parsedValue = Number.parseInt(rawValue, 10);
    if (Number.isNaN(parsedValue) || parsedValue < 0) {
        throw new AppError(`${name} must be a non-negative integer`, {
            code: 'INVALID_QUERY_PARAMETER',
            statusCode: 400,
        });
    }
    return parsedValue;
}
function resolveRouteErrorMessage(error) {
    if (error instanceof AppError) {
        return error.message;
    }
    if (error instanceof Error && error.message) {
        return error.message;
    }
    return 'Failed to clone repository';
}
router.get('/', asyncHandler(async (req, res) => {
    const userId = getAuthenticatedUserId(req);
    const projects = await getProjectsWithSessions(userId);
    res.json(projects);
}));
router.get('/archived', asyncHandler(async (req, res) => {
    const userId = getAuthenticatedUserId(req);
    const projects = await getArchivedProjectsWithSessions(userId);
    res.json(createApiSuccessResponse({ projects }));
}));
router.get('/:projectId/sessions', asyncHandler(async (req, res) => {
    const userId = getAuthenticatedUserId(req);
    const projectId = typeof req.params.projectId === 'string' ? req.params.projectId : '';
    const limit = parseNonNegativeIntQuery(req.query.limit, 'limit', 20);
    const offset = parseNonNegativeIntQuery(req.query.offset, 'offset', 0);
    const sessionsPage = await getProjectSessionsPage(userId, projectId, { limit, offset });
    res.json(sessionsPage);
}));
router.post('/create-project', asyncHandler(async (req, res) => {
    const requestBody = req.body;
    const projectPath = typeof requestBody.path === 'string' ? requestBody.path : '';
    const customName = typeof requestBody.customName === 'string' ? requestBody.customName : null;
    if (requestBody.workspaceType !== undefined) {
        throw new AppError('workspaceType is no longer supported. Use the single create-project flow.', {
            code: 'LEGACY_WORKSPACE_TYPE_UNSUPPORTED',
            statusCode: 400,
        });
    }
    if (requestBody.githubUrl || requestBody.githubTokenId || requestBody.newGithubToken) {
        throw new AppError('Repository cloning is not supported on create-project', {
            code: 'CLONE_NOT_SUPPORTED_ON_CREATE_PROJECT',
            statusCode: 400,
            details: 'Use /api/projects/clone-progress for cloning workflows',
        });
    }
    const projectCreationResult = await createProject({
        userId: getAuthenticatedUserId(req),
        username: getAuthenticatedUsername(req),
        projectPath,
        customName,
    });
    res.json({
        success: true,
        project: projectCreationResult.project,
        message: projectCreationResult.outcome === 'reactivated_archived'
            ? 'Archived project path reused successfully'
            : 'Project created successfully',
    });
}));
/**
 * One-time (or idempotent) migration: apply legacy `localStorage` starred projectIds to the DB, then clear client storage.
 */
router.post('/migrate-legacy-stars', asyncHandler(async (req, res) => {
    const userId = getAuthenticatedUserId(req);
    const projectIds = Array.isArray(req.body?.projectIds)
        ? req.body.projectIds.map((x) => String(x))
        : [];
    const { updated } = applyLegacyStarredProjectIds(userId, projectIds);
    res.json({ success: true, updated });
}));
router.get('/clone-progress', async (req, res) => {
    res.setHeader('Content-Type', 'text/event-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');
    res.flushHeaders();
    const sendEvent = (type, data) => {
        if (res.writableEnded) {
            return;
        }
        res.write(`data: ${JSON.stringify({ type, ...data })}\n\n`);
    };
    let cloneOperation = null;
    const closeListener = () => {
        cloneOperation?.cancel();
    };
    req.on('close', closeListener);
    try {
        const queryParams = req.query;
        const workspacePath = readQueryStringValue(queryParams.path);
        const githubUrl = readQueryStringValue(queryParams.githubUrl);
        const githubTokenId = readOptionalNumericQueryValue(queryParams.githubTokenId);
        const newGithubToken = readQueryStringValue(queryParams.newGithubToken) || null;
        const authenticatedUser = req.user;
        const userId = authenticatedUser?.id;
        if (userId === undefined || userId === null) {
            throw new AppError('Authenticated user is required', {
                code: 'AUTHENTICATION_REQUIRED',
                statusCode: 401,
            });
        }
        const username = getAuthenticatedUsername(req);
        cloneOperation = await startCloneProject({
            workspacePath,
            githubUrl,
            githubTokenId,
            newGithubToken,
            userId,
            username,
        }, {
            onProgress: (message) => {
                sendEvent('progress', { message });
            },
            onComplete: ({ project, message }) => {
                sendEvent('complete', { project, message });
            },
        });
        await cloneOperation.waitForCompletion;
    }
    catch (error) {
        sendEvent('error', { message: resolveRouteErrorMessage(error) });
    }
    finally {
        req.off('close', closeListener);
        if (!res.writableEnded) {
            res.end();
        }
    }
});
router.get('/:projectId/taskmaster', asyncHandler(async (req, res) => {
    const userId = getAuthenticatedUserId(req);
    const projectId = typeof req.params.projectId === 'string' ? req.params.projectId : '';
    const taskMasterDetails = await getProjectTaskMaster(userId, projectId);
    res.json(taskMasterDetails);
}));
router.put('/:projectId/rename', (req, res) => {
    try {
        const userId = getAuthenticatedUserId(req);
        const projectId = typeof req.params.projectId === 'string' ? req.params.projectId : '';
        const { displayName } = req.body;
        updateProjectDisplayName(userId, projectId, displayName);
        res.json({ success: true });
    }
    catch (error) {
        res.status(500).json({ error: error instanceof Error ? error.message : 'Failed to rename project' });
    }
});
router.post('/:projectId/toggle-star', asyncHandler(async (req, res) => {
    const userId = getAuthenticatedUserId(req);
    const projectId = typeof req.params.projectId === 'string' ? req.params.projectId : '';
    const { isStarred } = toggleProjectStar(userId, projectId);
    res.json({ success: true, isStarred });
}));
router.post('/:projectId/restore', asyncHandler(async (req, res) => {
    const userId = getAuthenticatedUserId(req);
    const projectId = typeof req.params.projectId === 'string' ? req.params.projectId : '';
    restoreArchivedProject(userId, projectId);
    res.json(createApiSuccessResponse({ projectId, isArchived: false }));
}));
/**
 * - `force` not set / false: archive project in DB only (`isArchived` = 1; hidden from active list).
 * - `force=true`: remove DB row, delete session rows for that path, remove all `*.jsonl` under the Claude project dir.
 */
router.delete('/:projectId', asyncHandler(async (req, res) => {
    const userId = getAuthenticatedUserId(req);
    const projectId = typeof req.params.projectId === 'string' ? req.params.projectId : '';
    const force = req.query.force === 'true';
    await deleteOrArchiveProject(userId, projectId, force);
    res.json({ success: true });
}));
export default router;
//# sourceMappingURL=projects.routes.js.map