import express, { Router, Request, Response } from 'express';
import * as ctp from '@commercetools/platform-sdk';
import { Project, BaseResource, PagedQueryResponse, ResourceIdentifier, QueryParam, ProductProjectionPagedSearchResponse, Product, ProductProjection, FacetResults, TermFacetResult, FilteredFacetResult, RangeFacetResult, ProductDraft, ReferenceTypeId, ShoppingListDraft, ShoppingList, CartDraft, Cart, CartAddLineItemAction, CartRemoveLineItemAction, CartSetBillingAddressAction, CartSetShippingMethodAction, CartSetCountryAction, CartSetCustomerEmailAction, CartSetCustomFieldAction, CartSetCustomTypeAction, CartSetLocaleAction, CartSetShippingAddressAction, LineItemDraft, LineItem, CustomerDraft, Customer, CustomerChangeEmailAction, CustomObjectDraft, CustomObject, InventoryEntryDraft, InventoryEntry, InventoryEntryChangeQuantityAction, InventoryEntrySetExpectedDeliveryAction, InventoryEntrySetCustomFieldAction, InventoryEntrySetCustomTypeAction, InventoryEntrySetRestockableInDaysAction, OrderFromCartDraft, Order, CartReference, OrderImportDraft, OrderAddPaymentAction, OrderChangeOrderStateAction, OrderChangePaymentStateAction, OrderTransitionStateAction, OrderSetBillingAddressAction, OrderSetCustomerEmailAction, OrderSetCustomFieldAction, OrderSetCustomTypeAction, OrderSetLocaleAction, OrderSetOrderNumberAction, OrderSetShippingAddressAction, OrderSetStoreAction, PaymentDraft, Payment, TransactionDraft, PaymentSetCustomFieldAction, PaymentSetCustomTypeAction, PaymentAddTransactionAction, PaymentChangeTransactionStateAction, PaymentTransitionStateAction, ProductPublishAction, ProductSetAttributeAction, ProductTypeDraft, ProductType, AttributeDefinitionDraft, AttributeDefinition, ProductTypeUpdateAction, ShippingMethodDraft, ShippingMethod, ShippingMethodUpdateAction, StateDraft, State, StateUpdateAction, TaxCategoryDraft, TaxCategory, TaxCategoryUpdateAction, ProductDiscountDraft, ProductDiscount, ProductDiscountUpdateAction, UpdateAction } from '@commercetools/platform-sdk';
import { ParsedQs } from 'qs';

declare type GetParams$1 = {
    expand?: string[];
};
declare type QueryParams$1 = {
    expand?: string | string[];
    sort?: string | string[];
    limit?: number;
    offset?: number;
    withTotal?: boolean;
    where?: string | string[];
    [key: string]: QueryParam;
};
declare abstract class AbstractStorage {
    abstract clear(): void;
    abstract assertStorage(typeId: RepositoryTypes): void;
    abstract all<RT extends RepositoryTypes>(projectKey: string, typeId: RT): Array<ResourceMap[RT]>;
    abstract add<RT extends RepositoryTypes>(projectKey: string, typeId: RT, obj: ResourceMap[RT]): void;
    abstract get<RT extends RepositoryTypes>(projectKey: string, typeId: RT, id: string, params?: GetParams$1): ResourceMap[RT] | null;
    abstract getByKey<RT extends RepositoryTypes>(projectKey: string, typeId: RT, key: string, params: GetParams$1): ResourceMap[RT] | null;
    abstract addProject(projectKey: string): Project;
    abstract getProject(projectKey: string): Project;
    abstract saveProject(project: Project): Project;
    abstract delete<RT extends RepositoryTypes>(projectKey: string, typeId: RT, id: string, params: GetParams$1): BaseResource | null;
    abstract query(projectKey: string, typeId: RepositoryTypes, params: QueryParams$1): PagedQueryResponse;
    abstract getByResourceIdentifier<RT extends RepositoryTypes>(projectKey: string, identifier: ResourceIdentifier): ResourceMap[RT] | undefined;
    abstract expand<T>(projectKey: string, obj: T, clause: undefined | string | string[]): T;
}

/**
 * This module implements the commercetools product projection filter expression.
 */

declare type RangeExpression = {
    type: 'RangeExpression';
    start?: number;
    stop?: number;
    match: (obj: any) => boolean;
};
declare type FilterExpression = {
    type: 'FilterExpression';
    match: (obj: any) => boolean;
};

declare type ProductProjectionSearchParams = {
    fuzzy?: boolean;
    fuzzyLevel?: number;
    markMatchingVariants?: boolean;
    staged?: boolean;
    filter?: string[];
    'filter.facets'?: string[];
    'filter.query'?: string[];
    facet?: string | string[];
    sort?: string | string[];
    limit?: number;
    offset?: number;
    withTotal?: boolean;
    priceCurrency?: string;
    priceCountry?: string;
    priceCustomerGroup?: string;
    priceChannel?: string;
    localeProjection?: string;
    storeProjection?: string;
    expand?: string | string[];
    [key: string]: QueryParam;
};
declare class ProductProjectionSearch {
    protected _storage: AbstractStorage;
    constructor(storage: AbstractStorage);
    search(projectKey: string, params: ProductProjectionSearchParams): ProductProjectionPagedSearchResponse;
    transform(product: Product): ProductProjection;
    getFacets(params: ProductProjectionSearchParams, products: Product[]): FacetResults;
    /**
     * TODO: This implemention needs the following additional features:
     *  - counting products
     *  - correct dataType
     */
    termFacet(facet: string, products: Product[], staged: boolean): TermFacetResult;
    filterFacet(source: string, filters: FilterExpression[] | undefined, products: Product[], staged: boolean): FilteredFacetResult;
    rangeFacet(source: string, ranges: RangeExpression[] | undefined, products: Product[], staged: boolean): RangeFacetResult;
}

declare class ProductProjectionRepository extends AbstractResourceRepository {
    protected _searchService: ProductProjectionSearch;
    constructor(storage: AbstractStorage);
    getTypeId(): RepositoryTypes;
    create(context: RepositoryContext, draft: ProductDraft): ProductProjection;
    query(context: RepositoryContext, params?: QueryParams): ctp.PagedQueryResponse;
    search(context: RepositoryContext, query: ParsedQs): ctp.ProductProjectionPagedSearchResponse;
    actions: {};
}

declare class ShoppingListRepository extends AbstractResourceRepository {
    getTypeId(): ReferenceTypeId;
    create(context: RepositoryContext, draft: ShoppingListDraft): ShoppingList;
}

declare abstract class AbstractService {
    protected abstract getBasePath(): string;
    abstract repository: AbstractResourceRepository;
    createStatusCode: number;
    constructor(parent: Router);
    extraRoutes(router: Router): void;
    registerRoutes(parent: Router): void;
    get(request: Request, response: Response): Response<any, Record<string, any>>;
    getWithId(request: Request, response: Response): Response<any, Record<string, any>>;
    getWithKey(request: Request, response: Response): Response<any, Record<string, any>>;
    deletewithId(request: Request, response: Response): Response<any, Record<string, any>>;
    deletewithKey(request: Request, response: Response): Response<any, Record<string, any>>;
    post(request: Request, response: Response): Response<any, Record<string, any>>;
    postWithId(request: Request, response: Response): Response<any, Record<string, any>>;
    postWithKey(request: Request, response: Response): Response<any, Record<string, any>>;
    protected _expandWithId(request: Request, resourceId: string): ctp.BaseResource | null;
    private _parseParam;
}

declare class CartRepository extends AbstractResourceRepository {
    getTypeId(): ReferenceTypeId;
    create(context: RepositoryContext, draft: CartDraft): Cart;
    getActiveCart(projectKey: string): Cart | undefined;
    actions: {
        addLineItem: (context: RepositoryContext, resource: Writable<Cart>, { productId, variantId, sku, quantity }: CartAddLineItemAction) => void;
        removeLineItem: (context: RepositoryContext, resource: Writable<Cart>, { lineItemId, quantity }: CartRemoveLineItemAction) => void;
        setBillingAddress: (context: RepositoryContext, resource: Writable<Cart>, { address }: CartSetBillingAddressAction) => void;
        setShippingMethod: (context: RepositoryContext, resource: Writable<Cart>, { shippingMethod }: CartSetShippingMethodAction) => void;
        setCountry: (context: RepositoryContext, resource: Writable<Cart>, { country }: CartSetCountryAction) => void;
        setCustomerEmail: (context: RepositoryContext, resource: Writable<Cart>, { email }: CartSetCustomerEmailAction) => void;
        setCustomField: (context: RepositoryContext, resource: Cart, { name, value }: CartSetCustomFieldAction) => void;
        setCustomType: (context: RepositoryContext, resource: Writable<Cart>, { type, fields }: CartSetCustomTypeAction) => void;
        setLocale: (context: RepositoryContext, resource: Writable<Cart>, { locale }: CartSetLocaleAction) => void;
        setShippingAddress: (context: RepositoryContext, resource: Writable<Cart>, { address }: CartSetShippingAddressAction) => void;
    };
    draftLineItemtoLineItem: (projectKey: string, draftLineItem: LineItemDraft, currency: string, country: string | undefined) => LineItem;
}

declare class CustomerRepository extends AbstractResourceRepository {
    getTypeId(): ReferenceTypeId;
    create(context: RepositoryContext, draft: CustomerDraft): Customer;
    getMe(context: RepositoryContext): Customer | undefined;
    actions: {
        changeEmail: (_context: RepositoryContext, resource: Writable<Customer>, { email }: CustomerChangeEmailAction) => void;
    };
}

declare class CustomObjectRepository extends AbstractResourceRepository {
    getTypeId(): ReferenceTypeId;
    create(context: RepositoryContext, draft: Writable<CustomObjectDraft>): CustomObject;
    getWithContainerAndKey(context: RepositoryContext, container: string, key: string): CustomObject | undefined;
}

declare class InventoryEntryRepository extends AbstractResourceRepository {
    getTypeId(): ReferenceTypeId;
    create(context: RepositoryContext, draft: InventoryEntryDraft): InventoryEntry;
    actions: {
        changeQuantity: (context: RepositoryContext, resource: Writable<InventoryEntry>, { quantity }: InventoryEntryChangeQuantityAction) => void;
        setExpectedDelivery: (context: RepositoryContext, resource: Writable<InventoryEntry>, { expectedDelivery }: InventoryEntrySetExpectedDeliveryAction) => void;
        setCustomField: (context: RepositoryContext, resource: InventoryEntry, { name, value }: InventoryEntrySetCustomFieldAction) => void;
        setCustomType: (context: RepositoryContext, resource: Writable<InventoryEntry>, { type, fields }: InventoryEntrySetCustomTypeAction) => void;
        setRestockableInDays: (context: RepositoryContext, resource: Writable<InventoryEntry>, { restockableInDays }: InventoryEntrySetRestockableInDaysAction) => void;
    };
}

declare class OrderRepository extends AbstractResourceRepository {
    getTypeId(): ReferenceTypeId;
    create(context: RepositoryContext, draft: OrderFromCartDraft): Order;
    createFromCart(context: RepositoryContext, cartReference: CartReference, orderNumber?: string): Order;
    import(context: RepositoryContext, draft: OrderImportDraft): Order;
    private lineItemFromImportDraft;
    private customLineItemFromImportDraft;
    getWithOrderNumber(context: RepositoryContext, orderNumber: string, params?: QueryParams): Order | undefined;
    actions: {
        addPayment: (context: RepositoryContext, resource: Writable<Order>, { payment }: OrderAddPaymentAction) => void;
        changeOrderState: (context: RepositoryContext, resource: Writable<Order>, { orderState }: OrderChangeOrderStateAction) => void;
        changePaymentState: (context: RepositoryContext, resource: Writable<Order>, { paymentState }: OrderChangePaymentStateAction) => void;
        transitionState: (context: RepositoryContext, resource: Writable<Order>, { state }: OrderTransitionStateAction) => void;
        setBillingAddress: (context: RepositoryContext, resource: Writable<Order>, { address }: OrderSetBillingAddressAction) => void;
        setCustomerEmail: (context: RepositoryContext, resource: Writable<Order>, { email }: OrderSetCustomerEmailAction) => void;
        setCustomField: (context: RepositoryContext, resource: Order, { name, value }: OrderSetCustomFieldAction) => void;
        setCustomType: (context: RepositoryContext, resource: Writable<Order>, { type, fields }: OrderSetCustomTypeAction) => void;
        setLocale: (context: RepositoryContext, resource: Writable<Order>, { locale }: OrderSetLocaleAction) => void;
        setOrderNumber: (context: RepositoryContext, resource: Writable<Order>, { orderNumber }: OrderSetOrderNumberAction) => void;
        setShippingAddress: (context: RepositoryContext, resource: Writable<Order>, { address }: OrderSetShippingAddressAction) => void;
        setStore: (context: RepositoryContext, resource: Writable<Order>, { store }: OrderSetStoreAction) => void;
    };
}

declare class PaymentRepository extends AbstractResourceRepository {
    getTypeId(): ReferenceTypeId;
    create(context: RepositoryContext, draft: PaymentDraft): Payment;
    transactionFromTransactionDraft: (draft: TransactionDraft, context: RepositoryContext) => {
        id: string;
        amount: ctp.TypedMoney;
        custom: ctp.CustomFields | undefined;
        timestamp?: string | undefined;
        type: ctp.TransactionType;
        interactionId?: string | undefined;
        state?: ctp.TransactionState | undefined;
    };
    actions: {
        setCustomField: (context: RepositoryContext, resource: Payment, { name, value }: PaymentSetCustomFieldAction) => void;
        setCustomType: (context: RepositoryContext, resource: Writable<Payment>, { type, fields }: PaymentSetCustomTypeAction) => void;
        addTransaction: (context: RepositoryContext, resource: Writable<Payment>, { transaction }: PaymentAddTransactionAction) => void;
        changeTransactionState: (_context: RepositoryContext, resource: Writable<Payment>, { transactionId, state }: PaymentChangeTransactionStateAction) => void;
        transitionState: (context: RepositoryContext, resource: Writable<Payment>, { state }: PaymentTransitionStateAction) => void;
    };
}

declare class ProductRepository extends AbstractResourceRepository {
    getTypeId(): ReferenceTypeId;
    create(context: RepositoryContext, draft: ProductDraft): Product;
    actions: {
        publish: (context: RepositoryContext, resource: Writable<Product>, { scope }: ProductPublishAction) => void;
        setAttribute: (context: RepositoryContext, resource: Writable<Product>, { variantId, sku, name, value, staged }: ProductSetAttributeAction) => void;
    };
}

declare class ProductTypeRepository extends AbstractResourceRepository {
    getTypeId(): ReferenceTypeId;
    create(context: RepositoryContext, draft: ProductTypeDraft): ProductType;
    attributeDefinitionFromAttributeDefinitionDraft: (_context: RepositoryContext, draft: AttributeDefinitionDraft) => AttributeDefinition;
    getWithKey(context: RepositoryContext, key: string): ProductType | undefined;
    actions: Partial<Record<ProductTypeUpdateAction['action'], (context: RepositoryContext, resource: Writable<ProductType>, action: any) => void>>;
}

declare class ShippingMethodRepository extends AbstractResourceRepository {
    getTypeId(): ReferenceTypeId;
    create(context: RepositoryContext, draft: ShippingMethodDraft): ShippingMethod;
    private _transformZoneRateDraft;
    private _transformShippingRate;
    actions: Partial<Record<ShippingMethodUpdateAction['action'], (context: RepositoryContext, resource: Writable<ShippingMethod>, action: any) => void>>;
}

declare class StateRepository extends AbstractResourceRepository {
    getTypeId(): ReferenceTypeId;
    create(context: RepositoryContext, draft: StateDraft): State;
    actions: Partial<Record<StateUpdateAction['action'], (context: RepositoryContext, resource: Writable<State>, action: any) => void>>;
}

declare class TaxCategoryRepository extends AbstractResourceRepository {
    getTypeId(): ReferenceTypeId;
    create(context: RepositoryContext, draft: TaxCategoryDraft): TaxCategory;
    private taxRateFromTaxRateDraft;
    getWithKey(context: RepositoryContext, key: string): TaxCategory | undefined;
    actions: Partial<Record<TaxCategoryUpdateAction['action'], (context: RepositoryContext, resource: Writable<TaxCategory>, action: any) => void>>;
}

declare class ProductDiscountRepository extends AbstractResourceRepository {
    getTypeId(): ReferenceTypeId;
    create(context: RepositoryContext, draft: ProductDiscountDraft): ProductDiscount;
    private transformValueDraft;
    getWithKey(context: RepositoryContext, key: string): ProductDiscount | undefined;
    actions: Partial<Record<ProductDiscountUpdateAction['action'], (context: RepositoryContext, resource: Writable<ProductDiscount>, action: any) => void>>;
}

declare type Writable<T> = {
    -readonly [P in keyof T]: Writable<T[P]>;
};
declare type RepositoryTypes = ReferenceTypeId | 'standalone-price' | 'product-projection';
declare type ServiceTypes = RepositoryTypes | 'my-cart' | 'my-order' | 'my-payment' | 'my-customer' | 'product-projection';
declare type Services = Partial<{
    [index in ServiceTypes]: AbstractService;
}>;
declare type ResourceMap = {
    cart: ctp.Cart;
    'cart-discount': ctp.CartDiscount;
    category: ctp.Category;
    channel: ctp.Channel;
    customer: ctp.Customer;
    'customer-group': ctp.CustomerGroup;
    'discount-code': ctp.DiscountCode;
    extension: ctp.Extension;
    'inventory-entry': ctp.InventoryEntry;
    'key-value-document': ctp.CustomObject;
    order: ctp.Order;
    'order-edit': ctp.OrderEdit;
    payment: ctp.Payment;
    product: ctp.Product;
    'product-discount': ctp.ProductDiscount;
    'product-projection': ctp.ProductProjection;
    'product-selection': ctp.ProductSelection;
    'product-price': ctp.StandalonePrice;
    'product-type': ctp.ProductType;
    review: ctp.Review;
    'shipping-method': ctp.ShippingMethod;
    'shopping-list': ctp.ShoppingList;
    'standalone-price': ctp.StandalonePrice;
    state: ctp.State;
    store: ctp.Store;
    subscription: ctp.Subscription;
    'tax-category': ctp.TaxCategory;
    type: ctp.Type;
    zone: ctp.Zone;
    'customer-email-token': never;
    'customer-password-token': never;
};
declare type RepositoryMap = {
    cart: CartRepository;
    'cart-discount': never;
    category: never;
    channel: never;
    customer: CustomerRepository;
    'customer-email-token': never;
    'customer-group': never;
    'customer-password-token': never;
    'discount-code': never;
    extension: never;
    'inventory-entry': InventoryEntryRepository;
    'key-value-document': CustomObjectRepository;
    order: OrderRepository;
    'order-edit': never;
    payment: PaymentRepository;
    product: ProductRepository;
    'product-projection': ProductProjectionRepository;
    'product-discount': ProductDiscountRepository;
    'product-type': ProductTypeRepository;
    review: never;
    'shipping-method': ShippingMethodRepository;
    'shopping-list': ShoppingListRepository;
    state: StateRepository;
    store: never;
    subscription: never;
    'tax-category': TaxCategoryRepository;
    type: ctp.Type;
    zone: ctp.Zone;
};

declare type QueryParams = {
    expand?: string[];
    where?: string[];
    offset?: number;
    limit?: number;
};
declare type GetParams = {
    expand?: string[];
};
declare type RepositoryContext = {
    projectKey: string;
    storeKey?: string;
};
declare abstract class AbstractRepository {
    protected _storage: AbstractStorage;
    protected actions: Partial<Record<any, (context: RepositoryContext, resource: any, action: any) => void>>;
    constructor(storage: AbstractStorage);
    abstract save({ projectKey }: RepositoryContext, resource: BaseResource | Project): void;
    processUpdateActions(context: RepositoryContext, resource: BaseResource | Project, actions: UpdateAction[]): BaseResource;
}
declare abstract class AbstractResourceRepository extends AbstractRepository {
    abstract create(context: RepositoryContext, draft: any): BaseResource;
    abstract getTypeId(): RepositoryTypes;
    constructor(storage: AbstractStorage);
    query(context: RepositoryContext, params?: QueryParams): ctp.PagedQueryResponse;
    get(context: RepositoryContext, id: string, params?: GetParams): BaseResource | null;
    getByKey(context: RepositoryContext, key: string, params?: GetParams): BaseResource | null;
    delete(context: RepositoryContext, id: string, params?: GetParams): BaseResource | null;
    save(context: RepositoryContext, resource: BaseResource): void;
}

declare class ProjectAPI {
    private projectKey;
    private _storage;
    private _services;
    constructor(projectKey: string, services: Services, storage: AbstractStorage);
    add(typeId: ReferenceTypeId, resource: ResourceMap[ReferenceTypeId]): void;
    get<RT extends RepositoryTypes>(typeId: RT, id: string, params?: GetParams): ResourceMap[RT];
    getRepository<RT extends keyof RepositoryMap>(typeId: ServiceTypes): RepositoryMap[RT];
}

declare type CommercetoolsMockOptions = {
    validateCredentials: boolean;
    enableAuthentication: boolean;
    defaultProjectKey: string | undefined;
    apiHost: RegExp | string;
    authHost: RegExp | string;
    silent: boolean;
};
declare type AppOptions = {
    silent?: boolean;
};
declare class CommercetoolsMock {
    app: express.Express;
    options: CommercetoolsMockOptions;
    private _storage;
    private _oauth2;
    private _nockScopes;
    private _services;
    private _projectService?;
    constructor(options?: Partial<CommercetoolsMockOptions>);
    start(): void;
    stop(): void;
    clear(): void;
    project(projectKey?: string): ProjectAPI;
    runServer(port?: number, options?: AppOptions): void;
    private createApp;
    private mockApiHost;
    private mockAuthHost;
}

declare const getBaseResourceProperties: () => {
    id: string;
    createdAt: string;
    lastModifiedAt: string;
    version: number;
};

export { CommercetoolsMock, CommercetoolsMockOptions, getBaseResourceProperties };
