import { Base } from '@airtable/blocks/models';
import {
  keys
} from 'lodash';

import {
  AirtableTableLinks,
  BaseSyncConfig
} from '../../models';
import {
  CustomerTypeName,
  InventoryLevelTypeName,
  LineItemTypeName,
  LocationTypeName,
  OrderTypeName,
  ProductTypeName,
  ProductVariantTypeName,
  ShopifyObjectType,
  ShopifyObjects
} from '../../models/shopify';
import { UnreachableCaseError } from '../../types';
import { isPresent } from '../../util';
import { AirtableField } from '../airtable';
import {
  ShopifyObjectAirtableFields,
  ShopifyObjectsAirtableFieldsMap
} from '../airtable/ShopifyObjectAirtableFields';

import { getCustomerAirtableFields } from './getCustomerAirtableFields';
import { getInventoryLevelAirtableFields } from './getInventoryLevelAirtableFields';
import { getLineItemAirtableFields } from './getLineItemAirtableFields';
import { getLocationAirtableFields } from './getLocationAirtableFields';
import { getOrderAirtableFields } from './getOrderAirtableFields';
import { getProductAirtableFields } from './getProductAirtableFields';
import { getProductVariantsAirtableFields } from './getProductVariantAirtableFields';

type CreateFieldsParam = Parameters<Base['createTableAsync']>[1];
type CreateFieldParam = Parameters<Base['createTableAsync']>[1][number];

export type getShopifyObjectAirtableFields<SOT extends ShopifyObjectType> = ({
  airtableTableLinks,
  baseSyncConfig
}: {
  airtableTableLinks: AirtableTableLinks;
  baseSyncConfig: BaseSyncConfig;
}) => ShopifyObjectsAirtableFieldsMap[SOT];

export type CreateAirtableFieldBody = AirtableField & {
  name: string;
};

function getShopifyObjectAirtableFields({
  shopifyObjectType,
  airtableTableLinks,
  baseSyncConfig,
}: {
  shopifyObjectType: ShopifyObjectType;
  airtableTableLinks: AirtableTableLinks;
  baseSyncConfig: BaseSyncConfig;
}):ShopifyObjectAirtableFields<ShopifyObjects[typeof shopifyObjectType]> {
  switch(shopifyObjectType) {
    case ProductTypeName: {
      return getProductAirtableFields({
        airtableTableLinks,
        baseSyncConfig
      });
    }
    case ProductVariantTypeName: {
      return getProductVariantsAirtableFields({
        airtableTableLinks,
        baseSyncConfig
      });
    }

    case OrderTypeName: {
      return getOrderAirtableFields({
        airtableTableLinks,
        baseSyncConfig
      });
    }

    case LocationTypeName: {
      return getLocationAirtableFields({
        airtableTableLinks,
        baseSyncConfig
      });
    }

    case InventoryLevelTypeName: {
      return getInventoryLevelAirtableFields({
        airtableTableLinks,
        baseSyncConfig
      });
    }

    case LineItemTypeName: {
      return getLineItemAirtableFields({
        airtableTableLinks,
        baseSyncConfig
      });
    }

    case CustomerTypeName: {
      return getCustomerAirtableFields({
        airtableTableLinks,
        baseSyncConfig
      });
    }

    default: {
      throw new UnreachableCaseError(shopifyObjectType);
    }
  }
}

export function getCreateFieldProps({
  shopifyObjectType,
  airtableTableLinks,
  baseSyncConfig,
  shopifyObjectKey
}: {
  shopifyObjectType: ShopifyObjectType;
  airtableTableLinks: AirtableTableLinks;
  baseSyncConfig: BaseSyncConfig;
  shopifyObjectKey: keyof ShopifyObjects[typeof shopifyObjectType]['props'];
}): CreateFieldParam | null {
  const shopifyObjectAirtableFields = getShopifyObjectAirtableFields({
    shopifyObjectType,
    airtableTableLinks,
    baseSyncConfig,
  });

  return getModifyAirtableFieldProps({
    shopifyObjectType,
    airtableFields: shopifyObjectAirtableFields,
    fieldKey: shopifyObjectKey
  });
}

export function getCreateTableProps({
  shopifyObjectType,
  airtableTableLinks,
  baseSyncConfig,
}: {
  shopifyObjectType: ShopifyObjectType;
  airtableTableLinks: AirtableTableLinks;
  baseSyncConfig: BaseSyncConfig;
}):CreateFieldsParam {
  const shopifyObjectAirtableFields = getShopifyObjectAirtableFields({
    shopifyObjectType,
    airtableTableLinks,
    baseSyncConfig,
  });

  return getModifyAirtableFieldsProps({
    shopifyObjectType,
    airtableFields: shopifyObjectAirtableFields
  });
}

export type getCreateTableProps = typeof getCreateTableProps;

function getModifyAirtableFieldsProps({
  shopifyObjectType,
  airtableFields,
}: {
  shopifyObjectType: ShopifyObjectType;
  airtableFields: ShopifyObjectAirtableFields<ShopifyObjects[typeof shopifyObjectType]>;
}): CreateAirtableFieldBody[] {

  return keys(airtableFields.fields)
    .map(fieldKey => getModifyAirtableFieldProps({
      shopifyObjectType,
      airtableFields,
      fieldKey: fieldKey as keyof typeof airtableFields.fields,
    }))
    .filter(isPresent);
}

// Rather than using a generic that extends ShopifyObjectType it's better to directly pass shopifyObjectType
// if we can. While it adds another param to the function signature, we can take advantage of control-flow analysis,
// which Typescript does on regular variables, but *not on type variables*.

// Related StackOverflow answer:
// https://stackoverflow.com/a/60476282/4744177
export function getModifyAirtableFieldProps({
  shopifyObjectType,
  airtableFields,
  fieldKey,
}: {
  shopifyObjectType: ShopifyObjectType;
  airtableFields: ShopifyObjectAirtableFields<ShopifyObjects[typeof shopifyObjectType]>;
  fieldKey: keyof ShopifyObjectAirtableFields<ShopifyObjects[typeof shopifyObjectType]>['fields'];
}): CreateAirtableFieldBody | null {
  const { fields } = airtableFields;
  const field = fields[fieldKey];

  if (field === null) {
    return null;
  }

  return {
    ...field,
    name: fieldKey as string
  };
}