#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import {
	ProductLiftClient,
	summarizePost,
	summarizePostDetail,
} from "./client.js";

const client = new ProductLiftClient();

const server = new McpServer( {
	name: "productlift-security-ninja",
	version: "1.0.0",
} );

server.tool(
	"productlift_list_statuses",
	"List workflow statuses for the Security Ninja ProductLift portal.",
	{},
	async () => {
		const statuses = await client.listStatuses();
		const lines = statuses.map(
			( status ) =>
				`${ status.id }\t${ status.name }\t(tab: ${ status.tab_name })`
		);

		return {
			content: [
				{
					type: "text",
					text: [
						`Portal: ${ process.env.PRODUCTLIFT_PORTAL_URL ?? "unknown" }`,
						`Statuses (${ statuses.length }):`,
						...lines,
					].join( "\n" ),
				},
			],
		};
	}
);

server.tool(
	"productlift_search_wishes",
	"Search ProductLift wishes/posts by keyword. Useful for finding old 2FA items or other backlog entries.",
	{
		query: z.string().min( 1 ).describe( "Search text, e.g. 2FA, two factor, MFA." ),
		status_id: z
			.number()
			.optional()
			.describe( "Optional status ID filter." ),
		tab: z
			.string()
			.optional()
			.describe( "Optional tab slug filter." ),
		max_results: z
			.number()
			.int()
			.min( 1 )
			.max( 200 )
			.optional()
			.describe( "Maximum matches to return (default 50)." ),
		search_in: z
			.enum( [ "title", "all" ] )
			.optional()
			.describe( "Search title only or title+description (default all)." ),
	},
	async ( args ) => {
		const result = await client.searchWishes( {
			query: args.query,
			statusId: args.status_id,
			tab: args.tab,
			maxResults: args.max_results,
			searchIn: args.search_in,
		} );

		if ( result.matches.length === 0 ) {
			return {
				content: [
					{
						type: "text",
						text: `No wishes matched query "${ args.query }".`,
					},
				],
			};
		}

		const body = result.matches
			.map( ( post, index ) => `${ index + 1 }. ${ summarizePost( post ) }` )
			.join( "\n\n" );

		return {
			content: [
				{
					type: "text",
					text: [
						`Query: ${ result.query }`,
						`Matches returned: ${ result.matches.length }`,
						result.truncated
							? "Note: results may be truncated; refine the query or increase max_results."
							: null,
						"",
						body,
					]
						.filter( Boolean )
						.join( "\n" ),
				},
			],
		};
	}
);

server.tool(
	"productlift_get_wish",
	"Get one ProductLift wish/post by its public ID (e.g. ye8jaV).",
	{
		wish_id: z.string().min( 1 ).describe( "ProductLift post ID." ),
	},
	async ( args ) => {
		const post = await client.getPost( args.wish_id );
		return {
			content: [
				{
					type: "text",
					text: summarizePostDetail( post ),
				},
			],
		};
	}
);

server.tool(
	"productlift_create_wish",
	"Create a new ProductLift wish/post. Use for feature requests from support tickets. Defaults to Feature category and Gathering votes status.",
	{
		title: z
			.string()
			.min( 2 )
			.max( 180 )
			.describe( "Wish title (2-180 characters)." ),
		description: z
			.string()
			.min( 1 )
			.describe( "Wish description / body text. Include source ticket URL when relevant." ),
		category: z
			.union( [ z.string().min( 1 ), z.number().int() ] )
			.optional()
			.describe( 'Category name or ID. Default: "Feature".' ),
		status: z
			.union( [ z.string().min( 1 ), z.number().int() ] )
			.optional()
			.describe( 'Initial status name or ID. Default: "Gathering votes".' ),
		excerpt: z
			.string()
			.max( 150 )
			.optional()
			.describe( "Optional short excerpt (max 150 chars)." ),
		priority: z
			.enum( [ "low", "medium", "high", "critical" ] )
			.optional()
			.describe( "Optional admin priority." ),
		is_internal: z
			.boolean()
			.optional()
			.describe( "If true, hide from non-admin users." ),
	},
	async ( args ) => {
		const statuses = await client.listStatuses();
		const statusTarget = client.resolveStatusId(
			statuses,
			args.status ?? "Gathering votes"
		);

		let categoryId: number | undefined;
		if ( args.category !== undefined ) {
			if ( typeof args.category === "number" ) {
				categoryId = args.category;
			} else {
				const categoriesResponse = await client.listCategories();
				const needle = args.category.trim().toLowerCase();
				const match = categoriesResponse.find(
					( category ) => category.name.toLowerCase() === needle
				);
				if ( ! match ) {
					const names = categoriesResponse
						.map( ( category ) => `${ category.id }: ${ category.name }` )
						.join( "; " );
					throw new Error(
						`Category "${ args.category }" was not found. Available: ${ names }`
					);
				}
				categoryId = match.id;
			}
		} else {
			const categoriesResponse = await client.listCategories();
			const feature = categoriesResponse.find(
				( category ) => category.name.toLowerCase() === "feature"
			);
			categoryId = feature?.id;
		}

		const created = await client.createPost( {
			title: args.title,
			description: args.description,
			categoryId,
			statusId: statusTarget.id,
			excerpt: args.excerpt,
			priority: args.priority,
			isInternal: args.is_internal,
		} );

		return {
			content: [
				{
					type: "text",
					text: [
						"Wish created.",
						summarizePostDetail( created ),
					].join( "\n\n" ),
				},
			],
		};
	}
);

server.tool(
	"productlift_update_wish_status",
	"Update only the status of a ProductLift wish/post. Requires explicit wish ID and target status name or ID.",
	{
		wish_id: z.string().min( 1 ).describe( "ProductLift post ID." ),
		status: z
			.union( [ z.string().min( 1 ), z.number().int() ] )
			.describe( "Target status name (exact or unique partial match) or status ID." ),
	},
	async ( args ) => {
		const statuses = await client.listStatuses();
		const targetStatus = client.resolveStatusId( statuses, args.status );
		const before = await client.getPost( args.wish_id );
		const after = await client.updatePostStatus( args.wish_id, targetStatus.id );

		return {
			content: [
				{
					type: "text",
					text: [
						"Status update completed.",
						`Wish: ${ before.id } - ${ before.title }`,
						`Before: ${ before.status.name } (${ before.status.id })`,
						`After: ${ after.status.name } (${ after.status.id })`,
						`URL: ${ after.url }`,
					].join( "\n" ),
				},
			],
		};
	}
);

async function main(): Promise<void> {
	const transport = new StdioServerTransport();
	await server.connect( transport );
}

main().catch( ( error: unknown ) => {
	const message = error instanceof Error ? error.message : String( error );
	console.error( `ProductLift MCP server failed: ${ message }` );
	process.exit( 1 );
} );
