import { NextResponse } from 'next/server';
import { prisma } from '@/lib/core/prisma';
import { createLogger } from '@/lib/core/logger';
import { deleteObjectIfExists } from '@/lib/storage/object-store-utils';
import { config as appConfig } from '@/config/app';
import { RUN_ACTIVE_STATUSES } from '@/types';
import { guardProjectRouteRequest } from '@/lib/security/project-route-access';
import { apiError } from '@/lib/security/api-route-standards';

const logger = createLogger('api:projects:id');

export async function GET(
    request: Request,
    { params }: { params: Promise<{ id: string }> }
) {
    const guard = await guardProjectRouteRequest({ request, params });
    if (!guard.ok) {
        return guard.response;
    }

    try {
        const { id } = guard.params;
        const { userId } = guard;
        const project = await prisma.project.findFirst({
            where: {
                id,
                team: {
                    memberships: {
                        some: { userId },
                    },
                },
            },
            select: {
                id: true,
                name: true,
                maxConcurrentRuns: true,
                teamId: true,
                createdByUserId: true,
                createdAt: true,
                updatedAt: true,
                team: {
                    select: {
                        memberships: {
                            where: { userId },
                            select: { role: true },
                            take: 1,
                        },
                    },
                },
            },
        });
        if (!project) {
            return apiError({
                status: 404,
                code: 'NOT_FOUND',
                error: 'Project not found',
            });
        }

        const currentUserRole = project.team.memberships[0]?.role ?? null;

        return NextResponse.json({
            id: project.id,
            name: project.name,
            maxConcurrentRuns: project.maxConcurrentRuns,
            teamId: project.teamId,
            createdByUserId: project.createdByUserId,
            createdAt: project.createdAt,
            updatedAt: project.updatedAt,
            maxConcurrentRunsLimit: appConfig.runner.maxProjectConcurrentRuns,
            canManageProject: true,
            canDeleteProject: currentUserRole === 'OWNER',
        });
    } catch (error) {
        logger.error('Failed to fetch project', error);
        return apiError({
            status: 500,
            code: 'INTERNAL_ERROR',
            error: 'Failed to fetch project',
        });
    }
}


export async function PUT(
    request: Request,
    { params }: { params: Promise<{ id: string }> }
) {
    const guard = await guardProjectRouteRequest({ request, params });
    if (!guard.ok) {
        return guard.response;
    }

    try {
        const { id } = guard.params;

        const body = await request.json() as {
            name?: unknown;
            maxConcurrentRuns?: unknown;
        };
        const hasName = body.name !== undefined;
        const hasMaxConcurrentRuns = body.maxConcurrentRuns !== undefined;

        if (!hasName && !hasMaxConcurrentRuns) {
            return apiError({
                status: 400,
                code: 'VALIDATION_ERROR',
                error: 'No updatable fields provided',
            });
        }

        const data: {
            name?: string;
            maxConcurrentRuns?: number;
        } = {};

        if (hasName) {
            const name = typeof body.name === 'string' ? body.name.trim() : '';
            if (!name) {
                return apiError({
                    status: 400,
                    code: 'VALIDATION_ERROR',
                    error: 'Name is required',
                });
            }
            data.name = name;
        }

        if (hasMaxConcurrentRuns) {
            const maxConcurrentRunsInput = body.maxConcurrentRuns;
            if (typeof maxConcurrentRunsInput !== 'number' || !Number.isInteger(maxConcurrentRunsInput)) {
                return apiError({
                    status: 400,
                    code: 'VALIDATION_ERROR',
                    error: 'maxConcurrentRuns must be an integer',
                });
            }
            if (maxConcurrentRunsInput < 1 || maxConcurrentRunsInput > appConfig.runner.maxProjectConcurrentRuns) {
                return apiError({
                    status: 400,
                    code: 'VALIDATION_ERROR',
                    error: `maxConcurrentRuns must be between 1 and ${appConfig.runner.maxProjectConcurrentRuns}`,
                });
            }
            data.maxConcurrentRuns = maxConcurrentRunsInput;
        }

        const project = await prisma.project.update({
            where: { id },
            data,
            select: {
                id: true,
                name: true,
                maxConcurrentRuns: true,
                teamId: true,
                createdByUserId: true,
                createdAt: true,
                updatedAt: true,
            },
        });

        return NextResponse.json({
            ...project,
            maxConcurrentRunsLimit: appConfig.runner.maxProjectConcurrentRuns,
        });
    } catch (error) {
        logger.error('Failed to update project', error);
        return apiError({
            status: 500,
            code: 'INTERNAL_ERROR',
            error: 'Failed to update project',
        });
    }
}

export async function DELETE(
    request: Request,
    { params }: { params: Promise<{ id: string }> }
) {
    const guard = await guardProjectRouteRequest({ request, params });
    if (!guard.ok) {
        return guard.response;
    }

    try {
        const { id } = guard.params;
        const { userId } = guard;
        const project = await prisma.project.findUnique({
            where: { id },
            select: { teamId: true },
        });
        if (!project) {
            return apiError({
                status: 404,
                code: 'NOT_FOUND',
                error: 'Project not found',
            });
        }

        const ownerMembership = await prisma.teamMembership.findUnique({
            where: {
                teamId_userId: {
                    teamId: project.teamId,
                    userId,
                },
            },
            select: { role: true },
        });

        if (ownerMembership?.role !== 'OWNER') {
            return apiError({
                status: 403,
                code: 'FORBIDDEN',
                error: 'Forbidden',
            });
        }

        const activeRuns = await prisma.testRun.findFirst({
            where: {
                testCase: {
                    projectId: id
                },
                status: {
                    in: [...RUN_ACTIVE_STATUSES]
                }
            }
        });

        if (activeRuns) {
            return apiError({
                status: 400,
                code: 'VALIDATION_ERROR',
                error: 'Cannot delete project while tests are running or queued',
            });
        }

        const testCases = await prisma.testCase.findMany({
            where: { projectId: id },
            select: {
                id: true,
                files: { select: { storedName: true } },
                configs: {
                    where: { type: 'FILE' },
                    select: { value: true }
                }
            }
        });

        const projectConfigFiles = await prisma.projectConfig.findMany({
            where: {
                projectId: id,
                type: 'FILE'
            },
            select: { value: true }
        });

        await prisma.project.delete({
            where: { id },
        });

        const objectKeys = [
            ...projectConfigFiles.map((config) => config.value),
            ...testCases.flatMap((testCase) => [
                ...testCase.files.map((file) => file.storedName),
                ...testCase.configs.map((config) => config.value),
            ])
        ];

        await Promise.all(objectKeys.map(async (objectKey) => {
            try {
                await deleteObjectIfExists(objectKey);
            } catch {
                logger.warn('Failed to delete object from storage', { objectKey });
            }
        }));

        return NextResponse.json({ success: true });
    } catch (error) {
        logger.error('Failed to delete project', error);
        return apiError({
            status: 500,
            code: 'INTERNAL_ERROR',
            error: 'Failed to delete project',
        });
    }
}
