import { CfnResource, IAsset } from 'aws-cdk-lib';
import { Construct, IConstruct } from 'constructs';
import { Grant, IGrantable, IRole } from 'aws-cdk-lib/aws-iam';
// eslint-disable-next-line import/no-cycle
import { TransformHostProvider } from './transform-host-provider';
import { AssetProvider } from './asset-provider';
import { IamResource } from 'aws-cdk-lib/aws-appsync';

// Auth Config Modes
export type AppSyncAuthMode = 'API_KEY' | 'AMAZON_COGNITO_USER_POOLS' | 'AWS_IAM' | 'OPENID_CONNECT' | 'AWS_LAMBDA';
export type AppSyncAuthConfiguration = {
  defaultAuthentication: AppSyncAuthConfigurationEntry;
  additionalAuthenticationProviders: Array<AppSyncAuthConfigurationEntry>;
};

export type AppSyncAuthConfigurationEntry =
  | AppSyncAuthConfigurationUserPoolEntry
  | AppSyncAuthConfigurationAPIKeyEntry
  | AppSyncAuthConfigurationIAMEntry
  | AppSyncAuthConfigurationOIDCEntry
  | AppSyncAuthConfigurationLambdaEntry;

export type AppSyncAuthConfigurationAPIKeyEntry = {
  authenticationType: 'API_KEY';
  apiKeyConfig?: ApiKeyConfig;
};
export type AppSyncAuthConfigurationUserPoolEntry = {
  authenticationType: 'AMAZON_COGNITO_USER_POOLS';
  userPoolConfig?: UserPoolConfig;
};
export type AppSyncAuthConfigurationIAMEntry = {
  authenticationType: 'AWS_IAM';
};

export type AppSyncAuthConfigurationOIDCEntry = {
  authenticationType: 'OPENID_CONNECT';
  openIDConnectConfig?: OpenIDConnectConfig;
};

export type AppSyncAuthConfigurationLambdaEntry = {
  authenticationType: 'AWS_LAMBDA';
  lambdaAuthorizerConfig?: LambdaConfig;
};

export interface ApiKeyConfig {
  description?: string;
  apiKeyExpirationDays: number;
  apiKeyExpirationDate?: Date;
}
export interface UserPoolConfig {
  userPoolId: string;
}
export interface OpenIDConnectConfig {
  name: string;
  issuerUrl: string;
  clientId?: string;
  iatTTL?: number;
  authTTL?: number;
}

export interface LambdaConfig {
  /**
   * Function name, excluding optional `-{env}` suffix.
   */
  lambdaFunction: string;

  /**
   * The ARN of an existing Lambda function. If provided, this will circumvent the ARN construction when building the API auth mode config.
   * The ARN must refer to the same function named in `lambdaFunction`.
   */
  lambdaArn?: string;

  /**
   * Optional auth response time to live.
   */
  ttlSeconds?: number;
}

export interface AppSyncFunctionConfigurationProvider extends IConstruct {
  readonly arn: string;
  readonly functionId: string;
}

export interface DataSourceOptions {
  /**
   * The name of the data source, overrides the id given by cdk
   *
   * @default - generated by cdk given the id
   */
  readonly name?: string;
  /**
   * The description of the data source
   *
   * @default - No description
   */
  readonly description?: string;
}

export interface SearchableDataSourceOptions extends DataSourceOptions {
  /**
   * ServiceRole for the Amazon OpenSearch
   */
  readonly serviceRole: IRole;
}

export enum TemplateType {
  INLINE = 'INLINE',
  S3_LOCATION = 'S3_LOCATION',
}
export interface InlineMappingTemplateProvider {
  type: TemplateType.INLINE;
  bind: (scope: Construct) => string;
  getTemplateHash: () => string;
}

export interface S3MappingTemplateProvider {
  type: TemplateType.S3_LOCATION;
  bind: (scope: Construct, assetProvider: AssetProvider) => string;
  getTemplateHash: () => string;
}

export interface S3MappingFunctionCodeProvider {
  type: TemplateType.S3_LOCATION;
  bind: (scope: Construct, assetProvider: AssetProvider) => IAsset;
}

export type MappingTemplateProvider = InlineMappingTemplateProvider | S3MappingTemplateProvider;

export interface GraphQLAPIProvider extends IConstruct {
  readonly apiId: string;
  readonly graphqlUrl: string;
  readonly host: TransformHostProvider;
  readonly name: string;
  readonly assetProvider: AssetProvider;

  // getDefaultAuthorization(): Readonly<AuthorizationMode>;
  // getAdditionalAuthorizationModes(): Readonly<AuthorizationMode[]>;
  addToSchema: (addition: string) => void;
  addSchemaDependency: (construct: CfnResource) => boolean;

  grant: (grantee: IGrantable, resources: IamResource, ...actions: string[]) => Grant;
  // /**
  //  *  Adds an IAM policy statement for Mutation access to this GraphQLApi to an IAM principal's policy.
  //  *
  //  * @param grantee The principal.
  //  * @param fields The fields to grant access to that are Mutations (leave blank for all).
  //  */
  grantMutation: (grantee: IGrantable, ...fields: string[]) => Grant;
  // /**
  //  *  Adds an IAM policy statement for Query access to this GraphQLApi to an IAM principal's policy.
  //  *
  //  * @param grantee The principal.
  //  * @param fields The fields to grant access to that are Queries (leave blank for all).
  //  */
  grantQuery: (grantee: IGrantable, ...fields: string[]) => Grant;
  // /**
  //  *  Adds an IAM policy statement for Subscription access to this GraphQLApi to an IAM principal's policy.
  //  *
  //  * @param grantee The principal.
  //  * @param fields The fields to grant access to that are Subscriptions (leave blank for all).
  //  */
  grantSubscription: (grantee: IGrantable, ...fields: string[]) => Grant;
}
