import { ValueOf } from '../../types';
import {
  InventoryLevelModelFragment,
  LocationModelFragment,
  OrderModelFragment,
  ProductModelFragment,
  ProductVariantModelFragment
} from '../../types/ShopifyGraphQLAdminAPI';

import {
  Customer,
  CustomerKey,
  CustomerKeys,
  CustomerTypeName
} from './Customer';
import {
  InventoryLevel,
  InventoryLevelKey,
  InventoryLevelKeys,
  InventoryLevelTypeName
} from './InventoryLevel';
import {
  LineItem,
  LineItemKey,
  LineItemKeys,
  LineItemTypeName
} from './LineItem';
import {
  Location,
  LocationKey,
  LocationKeys,
  LocationTypeName
} from './Location';
import {
  Order,
  OrderPropsKey,
  OrderPropsKeys,
  OrderTypeName
} from './Order';
import {
  Product,
  ProductKey,
  ProductKeys,
  ProductTypeName
} from './Product';
import {
  ProductVariant,
  ProductVariantKey,
  ProductVariantKeys,
  ProductVariantTypeName
} from './ProductVariant';

export * from './ShopifyObject';
export * from './Product';
export * from './Order';
export * from './ProductVariant';
export * from './Location';
export * from './InventoryLevel';
export * from './LineItem';
export * from './Customer';
export * from './shopifyObjectFamilies';
export * from './shopifyModelUtil';

export type ShopifyObjects = {
    [OrderTypeName]: Order;
    [ProductTypeName]: Product;
    [ProductVariantTypeName]: ProductVariant;
    [LocationTypeName]: Location;
    [InventoryLevelTypeName]: InventoryLevel;
    [LineItemTypeName]: LineItem;
    [CustomerTypeName]: Customer;
  };

const RESTBasedShopifyModelTypes = [LineItemTypeName, CustomerTypeName] as const;
type RESTBasedShopifyModelType = typeof RESTBasedShopifyModelTypes[number];

/**
 * We draw a distinction between the Shopify Object model and the raw result we retrieve from the GraphQL API.
 * The GraphQL API response should be saved *as is* onto ShopifyEntity.shopify_object. The Shopify object
 * can always be derived from the raw query result.
 * 
 * We're going to start deviating from the above as we start bringing REST models in
 */
export type ShopifyObjectQueryResults = {
    [ProductTypeName]: ProductModelFragment;
    [ProductVariantTypeName]: ProductVariantModelFragment;
    [OrderTypeName]: OrderModelFragment;
    [LocationTypeName]: LocationModelFragment;
    [InventoryLevelTypeName]: InventoryLevelModelFragment;
    [LineItemTypeName]: LineItem['props'];
    [CustomerTypeName]: Customer['props'];
  };

export type ShopifyObjectQueryResult = ValueOf<ShopifyObjectQueryResults>;
export type ShopifyObjectGQLQueryResult = ValueOf<Omit<ShopifyObjectQueryResults, RESTBasedShopifyModelType>>;

export type ShopifyObjectGQLQueryResults = Omit<ShopifyObjectQueryResults, RESTBasedShopifyModelType>;

export const GQLShopifyObjectTypes = [
  ProductTypeName,
  ProductVariantTypeName,
  OrderTypeName,
  LocationTypeName,
  InventoryLevelTypeName,
] as const;
export const RESTShopifyObjectTypes = [
  LineItemTypeName,
  CustomerTypeName
] as const;
export const ShopifyObjectTypes = [
  ...GQLShopifyObjectTypes,
  ...RESTShopifyObjectTypes,
] as const;
export type ShopifyObjectType = typeof ShopifyObjectTypes[number];

export type GQLShopifyObjectType = typeof GQLShopifyObjectTypes[number];
export type RESTShopifyObjectType = typeof RESTShopifyObjectTypes[number];
export type ShopifyObject = Product | ProductVariant | Order | Location | InventoryLevel | LineItem | Customer;

/**
 * Currently the same as GQL but may differ in the future
 * e.g.
 * 1. dedicated customer job is something we probably want to do
 * 2. we might want to put in product variants inside the products job (probably not but a possibility)
 * 
 */
export const TopLevelBulkJobShopifyObjectTypes = [
  ...GQLShopifyObjectTypes,
] as const;

/**
 * Types that have their own dedicated Shopify Bulk Job
 * Customers and LineItems do not atm
 * Customers probably should in the future
 */
export type TopLevelBulkJobShopifyObjectType = Exclude<ShopifyObjectType, RESTBasedShopifyModelType>;

/**
 * The order used when syncing entities from Shopify and checking for the existence of tables.
 */
export const ModelSyncOrder: Readonly<ShopifyObjectType[]> = [
  LocationTypeName,
  ProductTypeName,
  ProductVariantTypeName,
  OrderTypeName,
  InventoryLevelTypeName
] as const;

export const AirtableSyncOrder: Readonly<ShopifyObjectType[]> = [
  LocationTypeName,
  ProductTypeName,
  ProductVariantTypeName,
  OrderTypeName,
  LineItemTypeName,
  CustomerTypeName,
  InventoryLevelTypeName
] as const;

export function getSyncedObjectTypesInSyncOrder(syncedObjectTypes: ShopifyObjectType[]): ShopifyObjectType[] {
  return AirtableSyncOrder.filter(shopifyObjectType => syncedObjectTypes.includes(shopifyObjectType));
}

export const ShopifyObjectDescriptions:Record<ShopifyObjectType, string> = {
  [ProductTypeName]: 'The goods, digital downloads, services, and gift cards that you sell.',
  [ProductVariantTypeName]: 'You add variants to a product that comes in more than one option, such as size or color. Each combination of option values for a product can be a variant for that product.',
  [OrderTypeName]: '',
  [CustomerTypeName]: 'Represents information about a customer of the shop, such as the customer\'s contact details, their order history, and whether they\'ve agreed to receive marketing material by email.',
  [LineItemTypeName]: 'Represents a single line item on an order.',
  [LocationTypeName]: 'Represents the location where the physical good resides.',
  [InventoryLevelTypeName]: 'Represents the inventory quantity of an inventory item at a specific location.'
};

const RelationTypes = ['HasMany', 'BelongsTo', 'HasOne'] as const;
  type RelationType = typeof RelationTypes[number];

  type RelatedPropertyMap = {
    [T in ShopifyObjectType]: {
      [RelatedObjectType in ShopifyObjectType]?: {
        relation: RelationType;

        /**
         * The key of the Shopify object that will be used to create an Airtable record link.
         */
        key?: keyof ShopifyObjects[T]['props'];
      }
    }
  };

export const ShopifyObjectRelations:RelatedPropertyMap = {
  [OrderTypeName]: {
  },
  [CustomerTypeName]: {
  },
  [LocationTypeName]: {
    [InventoryLevelTypeName]: {
      relation: 'HasMany'
    }
  },
  [ProductTypeName]: {
    [ProductVariantTypeName]: {
      relation: 'HasMany',
    }
  },
  [ProductVariantTypeName]: {
    [ProductTypeName]: {
      relation: 'BelongsTo',
      key: 'product'
    }
  },
  [InventoryLevelTypeName]: {
    [LocationTypeName]: {
      relation: 'BelongsTo',
      key: 'location'
    }
  },
  [LineItemTypeName]: {
    [OrderTypeName]: {
      relation: 'BelongsTo',
      key: 'order'
    }
  }
} as const;

export const ModelPropsKeys = {
  [OrderTypeName]: OrderPropsKeys,
  [ProductTypeName]: ProductKeys,
  [ProductVariantTypeName]: ProductVariantKeys,
  [LocationTypeName]: LocationKeys,
  [InventoryLevelTypeName]: InventoryLevelKeys,
  [LineItemTypeName]: LineItemKeys,
  [CustomerTypeName]: CustomerKeys,
} as const;

export type ModelPropsKey = {
  [OrderTypeName]: OrderPropsKey;
  [ProductTypeName]: ProductKey;
  [ProductVariantTypeName]: ProductVariantKey;
  [LocationTypeName]: LocationKey;
  [InventoryLevelTypeName]: InventoryLevelKey;
  [LineItemTypeName]: LineItemKey;
  [CustomerTypeName]: CustomerKey;
};