import { z } from 'zod';
import { type OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
import {
	TokenResponseSchema,
	TokenResponseWrapperSchema,
	IntrospectionRequestSchema,
	IntrospectionResponseSchema,
	RevocationRequestSchema,
	UserInfoResponseSchema,
	OIDCDiscoverySchema,
	OAuthErrorResponseSchema,
	OIDCErrorResponseSchema,
} from '#contracts/oidc/schema';
import { ErrorResponseSchema } from '#contracts/shared';

export function registerOidcPaths(registry: OpenAPIRegistry): void {
	// =========================================================================
	// OIDC Discovery
	// =========================================================================

	registry.registerPath({
		method: 'get',
		path: '/.well-known/openid-configuration',
		operationId: 'getOidcDiscovery',
		tags: ['OIDC'],
		summary: 'OIDC Discovery Document',
		description:
			'Returns the OpenID Connect discovery document. ' +
			'This is the standard OIDC metadata endpoint that clients use to ' +
			'discover the authorization server capabilities and endpoints.',
		security: [],
		responses: {
			200: {
				description: 'OIDC discovery document',
				content: { 'application/json': { schema: OIDCDiscoverySchema } },
			},
		},
	});

	// =========================================================================
	// JWKS
	// =========================================================================

	registry.registerPath({
		method: 'get',
		path: '/oauth/jwks',
		operationId: 'getJwks',
		tags: ['OIDC'],
		summary: 'JSON Web Key Set',
		description:
			'Returns the public keys used by the authorization server to verify ' +
			'JWT signatures. Clients use these keys to validate ID tokens and access tokens.',
		security: [],
		responses: {
			200: {
				description: 'JWKS document',
				content: { 'application/json': { schema: z.object({}).passthrough().openapi('JWKS') } },
			},
		},
	});

	// =========================================================================
	// Token endpoint
	// =========================================================================

	registry.registerPath({
		method: 'post',
		path: '/oauth/token',
		operationId: 'token',
		tags: ['OIDC'],
		summary: 'Token endpoint',
		description:
			'Exchange authorization codes for tokens, refresh tokens, ' +
			'or request tokens directly using the password grant or client credentials grant. ' +
			'Supports grant types: authorization_code, refresh_token, password, client_credentials.',
		security: [],
		request: {
			body: {
				required: true,
				content: {
					'application/x-www-form-urlencoded': {
						schema: z
							.object({
								grant_type: z.enum([
									'authorization_code',
									'refresh_token',
									'password',
									'client_credentials',
								]),
								code: z.string().optional(),
								redirect_uri: z.string().url().optional(),
								client_id: z.string().optional(),
								client_secret: z.string().optional(),
								refresh_token: z.string().optional(),
								username: z.string().optional(),
								password: z.string().optional(),
								scope: z.string().optional(),
							})
							.openapi('TokenRequest'),
					},
				},
			},
		},
		responses: {
			200: {
				description: 'Token response',
				content: { 'application/json': { schema: TokenResponseWrapperSchema } },
			},
			400: {
				description: 'OAuth2 error — invalid request',
				content: { 'application/json': { schema: OAuthErrorResponseSchema } },
			},
			401: {
				description: 'OAuth2 error — invalid client credentials',
				content: { 'application/json': { schema: OAuthErrorResponseSchema } },
			},
		},
	});

	// =========================================================================
	// UserInfo
	// =========================================================================

	registry.registerPath({
		method: 'get',
		path: '/oauth/userinfo',
		operationId: 'getUserInfo',
		tags: ['OIDC'],
		summary: 'UserInfo endpoint',
		description:
			'Returns the authenticated user\'s claims. ' +
			'Requires a valid access token with the "openid" scope. ' +
			'This endpoint is part of the OpenID Connect Core spec.',
		security: [{ BearerAuth: [] }],
		responses: {
			200: {
				description: 'UserInfo claims',
				content: { 'application/json': { schema: UserInfoResponseSchema } },
			},
			401: {
				description: 'Unauthorized — invalid or missing token',
				content: { 'application/json': { schema: OIDCErrorResponseSchema } },
			},
		},
	});

	// =========================================================================
	// Introspection
	// =========================================================================

	registry.registerPath({
		method: 'post',
		path: '/oauth/introspect',
		operationId: 'introspectToken',
		tags: ['OIDC'],
		summary: 'Token introspection',
		description:
			'Allows a protected resource to query the authorization server to determine ' +
			'the active state and meta-information of a token. Used for validating tokens.',
		security: [{ BearerAuth: [] }],
		request: {
			body: {
				required: true,
				content: {
					'application/x-www-form-urlencoded': {
						schema: IntrospectionRequestSchema,
					},
				},
			},
		},
		responses: {
			200: {
				description: 'Token introspection result',
				content: { 'application/json': { schema: IntrospectionResponseSchema } },
			},
			400: {
				description: 'Bad request',
				content: { 'application/json': { schema: ErrorResponseSchema } },
			},
			401: {
				description: 'Unauthorized',
				content: { 'application/json': { schema: ErrorResponseSchema } },
			},
		},
	});

	// =========================================================================
	// Revocation
	// =========================================================================

	registry.registerPath({
		method: 'post',
		path: '/oauth/revoke',
		operationId: 'revokeToken',
		tags: ['OIDC'],
		summary: 'Token revocation',
		description:
			'Revokes an access token or refresh token. After revocation, ' +
			'the token can no longer be used to access protected resources.',
		security: [],
		request: {
			body: {
				required: true,
				content: {
					'application/x-www-form-urlencoded': {
						schema: RevocationRequestSchema,
					},
				},
			},
		},
		responses: {
			200: {
				description: 'Token revoked successfully',
			},
			400: {
				description: 'Bad request',
				content: { 'application/json': { schema: ErrorResponseSchema } },
			},
		},
	});

	// =========================================================================
	// Logout / End Session
	// =========================================================================

	registry.registerPath({
		method: 'get',
		path: '/oauth/logout',
		operationId: 'logoutGet',
		tags: ['OIDC'],
		summary: 'Logout (GET)',
		description:
			'Initiates the logout flow. Supports id_token_hint and post_logout_redirect_uri parameters. ' +
			'This is the RP-initiated logout endpoint as defined in OpenID Connect Session Management.',
		security: [],
		request: {
			query: z.object({
				id_token_hint: z.string().optional(),
				post_logout_redirect_uri: z.string().url().optional(),
				state: z.string().optional(),
			}),
		},
		responses: {
			302: { description: 'Redirect to post_logout_redirect_uri or default logout page' },
		},
	});

	registry.registerPath({
		method: 'post',
		path: '/oauth/logout',
		operationId: 'logoutPost',
		tags: ['OIDC'],
		summary: 'Logout (POST)',
		description: 'Initiates logout via POST. Supports OpenID Connect front-channel logout.',
		security: [],
		responses: {
			200: { description: 'Logout processed' },
			400: {
				description: 'Bad request',
				content: { 'application/json': { schema: OIDCErrorResponseSchema } },
			},
		},
	});
}
