import { LdapSyncDto, UpdateLdapConfigurationDto } from '@n8n/api-types';
import { Container } from '@n8n/di';

import { ResponseError } from '@/errors/response-errors/abstract/response.error';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { getLdapSynchronizationsWithCount } from '@/modules/ldap.ee/helpers.ee';
import { LdapConnectionError, LdapRejectionError } from '@/modules/ldap.ee/ldap.errors';
import { LdapService } from '@/modules/ldap.ee/ldap.service.ee';

import {
	toLdapConfigurationResponse,
	toLdapConfigUpdate,
	toLdapSyncHistoryResponse,
} from './ldap.mapper';
import type { LdapRequest } from '../../../types';
import type { PublicAPIEndpoint } from '../../shared/handler.types';
import {
	apiKeyHasScopeWithGlobalScopeFallback,
	isLicensed,
	validCursor,
} from '../../shared/middlewares/global.middleware';
import { encodeNextCursor } from '../../shared/services/pagination.service';

type LdapHandlers = {
	getLdapConfiguration: PublicAPIEndpoint<LdapRequest.GetConfig>;
	updateLdapConfiguration: PublicAPIEndpoint<LdapRequest.UpdateConfig>;
	getLdapSync: PublicAPIEndpoint<LdapRequest.GetSync>;
	runLdapSync: PublicAPIEndpoint<LdapRequest.RunSync>;
};

const ldapHandlers: LdapHandlers = {
	getLdapConfiguration: [
		isLicensed('feat:ldap'),
		apiKeyHasScopeWithGlobalScopeFallback({ scope: 'ldap:manage' }),
		async (_req, res) => {
			const ldapService = Container.get(LdapService);
			const config = await ldapService.loadConfig();

			return res.json(toLdapConfigurationResponse(config));
		},
	],

	updateLdapConfiguration: [
		isLicensed('feat:ldap'),
		apiKeyHasScopeWithGlobalScopeFallback({ scope: 'ldap:manage' }),
		async (req, res) => {
			const payload = UpdateLdapConfigurationDto.safeParse(req.body);
			if (!payload.success) {
				throw new BadRequestError(payload.error.errors[0]?.message ?? 'Invalid request body');
			}

			const ldapService = Container.get(LdapService);
			const current = await ldapService.loadConfig();

			const updated = toLdapConfigUpdate(payload.data, current);
			await ldapService.updateConfig(updated);

			const reloaded = await ldapService.loadConfig();
			return res.json(toLdapConfigurationResponse(reloaded));
		},
	],

	getLdapSync: [
		isLicensed('feat:ldap'),
		apiKeyHasScopeWithGlobalScopeFallback({ scope: 'ldap:sync' }),
		validCursor,
		async (req, res) => {
			const { offset = 0, limit = 100 } = req.query;

			const [rows, count] = await getLdapSynchronizationsWithCount(offset, limit);

			return res.json({
				data: rows.map(toLdapSyncHistoryResponse),
				nextCursor: encodeNextCursor({ offset, limit, numberOfTotalRecords: count }),
			});
		},
	],

	runLdapSync: [
		isLicensed('feat:ldap'),
		apiKeyHasScopeWithGlobalScopeFallback({ scope: 'ldap:sync' }),
		async (req, res) => {
			const payload = LdapSyncDto.safeParse(req.body);
			if (!payload.success) {
				throw new BadRequestError(payload.error.errors[0]?.message ?? 'Invalid request body');
			}

			let syncHistory;
			try {
				syncHistory = await Container.get(LdapService).runSync(payload.data.type);
			} catch (e) {
				if (e instanceof ResponseError) throw e;
				if (e instanceof LdapRejectionError || e instanceof LdapConnectionError) {
					throw new BadRequestError(e.message);
				}
				throw e;
			}

			return res.json(toLdapSyncHistoryResponse(syncHistory));
		},
	],
};

export = ldapHandlers;
