import * as _nuxt_schema from '@nuxt/schema';
import { Nuxt, HookResult } from '@nuxt/schema';
import { StorefrontOperations } from '@konkonam/nuxt-shopify/storefront';
import { AdminOperations } from '@konkonam/nuxt-shopify/admin';
import { ApiClientConfig, AllOperations, ApiClient, ApiClientRequestStream, ApiClientRequestOptions, ClientResponse, ReturnData, ResponseErrors } from '@shopify/graphql-client';
import { ConsolaOptions } from 'consola';

declare enum ShopifyClientType {
    Storefront = "storefront",
    Admin = "admin"
}

type GenericApiClient<Operations extends AllOperations> = ApiClient<ApiClientConfig, Operations> & {
    requestStream: ApiClientRequestStream<Operations>
}

type GenericApiClientConfig = ApiClientConfig & {
    logger?: Partial<ConsolaOptions>
}

type ApiClientCustomConfig = {
    skipCodegen?: boolean
    sandbox?: boolean
    documents?: string[]
}

type StorefrontApiClientCustomConfig = {
    publicAccessToken?: string
    privateAccessToken?: string
    proxy?: boolean | string
    mock?: boolean
}

type AdminApiClientCustomConfig = {
    accessToken: string
}

type ShopifyApiClientConfig = Omit<ApiClientConfig, 'storeDomain' | 'apiUrl' | 'headers'> & {
    headers?: Record<string, string | string[]>
}

type ShopifyStorefrontConfig = ShopifyApiClientConfig & ApiClientCustomConfig & StorefrontApiClientCustomConfig
type ShopifyAdminConfig = ShopifyApiClientConfig & ApiClientCustomConfig & AdminApiClientCustomConfig

type ShopifyClientConfig = ShopifyStorefrontConfig | ShopifyAdminConfig

// Fully resolved shopify config
type ShopifyConfig<
    S = ShopifyStorefrontConfig,
    A = ShopifyAdminConfig,
> = {
    name: string
    logger?: Partial<ConsolaOptions>
    autoImports?: {
        graphql?: boolean
        storefront?: boolean
        admin?: boolean
    }
    errors?: {
        throw?: boolean
    }
    clients: {
        [ShopifyClientType.Storefront]?: S
        [ShopifyClientType.Admin]?: A
    }
}

// Optional public config for client side usage
type PublicShopifyConfig<S = ShopifyStorefrontConfig> = {
    name: string
    logger?: Partial<ConsolaOptions>
    errors?: {
        throw?: boolean
    }
    clients: {
        [ShopifyClientType.Storefront]?: Omit<S, 'privateAccessToken' | 'skipCodegen' | 'sandbox' | 'documents'>
    }
}

type ModuleOptions = ShopifyConfig<
    Partial<Omit<ShopifyStorefrontConfig, 'storeDomain' | 'logger' | 'customFetchApi'>>,
    Partial<Omit<ShopifyAdminConfig, 'storeDomain' | 'logger' | 'customFetchApi'>>
>

type ShopifyConfigHookParams = {
    nuxt: Nuxt
    config: ShopifyConfig
}

type ShopifyClientOptionHookParams = {
    config: GenericApiClientConfig
}

type ShopifyClientHookParams<Operations extends AllOperations> = {
    client: GenericApiClient<Operations>
}

type ShopifyClientRequestHookParams<Operation extends keyof Operations, Operations extends AllOperations = AllOperations> = {
    operation: Operation
    options?: ApiClientRequestOptions<Operation, Operations>
}

type ShopifyClientResponseHookParams<Operation extends keyof Operations, Operations extends AllOperations = AllOperations> = {
    response: ClientResponse<ReturnData<Operation, Operations>>
    operation: Operation
    options?: ApiClientRequestOptions<Operation, Operations>
}

type ShopifyErrorHookParams = {
    errors: ResponseErrors
}

type ShopifyTemplateHookParams = {
    nuxt: Nuxt
    config: Record<string, unknown>
}

type SandboxConfig = {
    url: string
    headers: Record<string, string>
}

declare module '@nuxt/schema' {
    interface RuntimeConfig {
        shopify?: ModuleOptions
        _shopify?: ShopifyConfig
        _sandbox?: Record<string, SandboxConfig>
    }

    interface PublicRuntimeConfig {
        shopify?: Omit<ModuleOptions, 'clients'> & {
            clients?: {
                storefront: Partial<Omit<ShopifyStorefrontConfig, 'storeDomain' | 'logger' | 'customFetchApi' | 'privateAccessToken'>>
            }
        }

        _shopify?: PublicShopifyConfig
    }

    interface NuxtHooks {
        /**
         * Called before the config is persisted into the runtime config
         */
        'shopify:config': ({ nuxt, config }: ShopifyConfigHookParams) => HookResult

        /**
         * Called before the storefront introspection schema is generated
         */
        'storefront:generate:introspection': ({ nuxt, config }: ShopifyTemplateHookParams) => HookResult

        /**
         * Called before the storefront types are generated
         */
        'storefront:generate:types': ({ nuxt, config }: ShopifyTemplateHookParams) => HookResult

        /**
         * Called before the storefront operations are generated
         */
        'storefront:generate:operations': ({ nuxt, config }: ShopifyTemplateHookParams) => HookResult

        /**
         * Called before the admin introspection schema is generated
         */
        'admin:generate:introspection': ({ nuxt, config }: ShopifyTemplateHookParams) => HookResult

        /**
         * Called before the admin types are generated
         */
        'admin:generate:types': ({ nuxt, config }: ShopifyTemplateHookParams) => HookResult

        /**
         * Called before the admin operations are generated
         */
        'admin:generate:operations': ({ nuxt, config }: ShopifyTemplateHookParams) => HookResult
    }
}

declare module '#app' {
    interface RuntimeNuxtHooks {
        /**
         * Called before the storefront client is created within nuxt
         */
        'storefront:client:configure': ({ config }: ShopifyClientOptionHookParams) => HookResult

        /**
         * Called after the storefront client is created within nuxt
         */
        'storefront:client:create': ({ client }: ShopifyClientHookParams<StorefrontOperations>) => HookResult

        /**
         * Called before the storefront client sends a request within nuxt
         */
        'storefront:client:request': <Operation extends keyof AllOperations>({ operation, options }: ShopifyClientRequestHookParams<Operation, AllOperations>) => HookResult

        /**
         * Called after the storefront client receives a response within nuxt
         */
        'storefront:client:response': <Operation extends keyof AllOperations>({ response, operation, options }: ShopifyClientResponseHookParams<Operation, AllOperations>) => HookResult

        /**
         * Called when the storefront client throws an error within nuxt
         */
        'storefront:client:errors': ({ errors }: ShopifyErrorHookParams) => HookResult
    }
}

declare module 'nitropack' {
    interface NitroRuntimeHooks {
        /**
         * Called before the storefront client is created within nitro
         */
        'storefront:client:configure': ({ config }: ShopifyClientOptionHookParams) => HookResult

        /**
         * Called after the storefront client is created within nitro
         */
        'storefront:client:create': ({ client }: ShopifyClientHookParams<StorefrontOperations>) => HookResult

        /**
         * Called before the storefront client sends a request within nitro
         */
        'storefront:client:request': <Operation extends keyof AllOperations>({ operation, options }: ShopifyClientRequestHookParams<Operation, AllOperations>) => HookResult

        /**
         * Called after the storefront client receives a response within nitro
         */
        'storefront:client:response': <Operation extends keyof AllOperations>({ response, operation, options }: ShopifyClientResponseHookParams<Operation, AllOperations>) => HookResult

        /**
         * Called when the storefront client throws an error within nitro
         */
        'storefront:client:errors': ({ errors }: ShopifyErrorHookParams) => HookResult

        /**
         * Called before the admin client is created within nitro
         */
        'admin:client:configure': ({ config }: ShopifyClientOptionHookParams) => HookResult

        /**
         * Called after the admin client is created within nitro
         */
        'admin:client:create': ({ client }: ShopifyClientHookParams<AdminOperations>) => HookResult

        /**
         * Called before the admin client sends a request within nitro
         */
        'admin:client:request': <Operation extends keyof AllOperations>({ operation, options }: ShopifyClientRequestHookParams<Operation, AllOperations>) => HookResult

        /**
         * Called after the admin client receives a response within nitro
         */
        'admin:client:response': <Operation extends keyof AllOperations>({ response, operation, options }: ShopifyClientResponseHookParams<Operation, AllOperations>) => HookResult

        /**
         * Called when the admin client throws an error within nitro
         */
        'admin:client:errors': ({ errors }: ShopifyErrorHookParams) => HookResult
    }
}

declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;

declare const setupClient: (nuxt: Nuxt, config: ShopifyConfig, clientType: ShopifyClientType, clientConfig: ShopifyClientConfig) => void;
declare const setupStorefrontFeatures: (nuxt: Nuxt, config: ShopifyConfig, clientConfig: ShopifyStorefrontConfig) => void;

export { _default as default, setupClient, setupStorefrontFeatures };
