import { ConfigurationError } from "@pipedream/platform";
import {
  DEFAULT_COMPANY_PROPERTIES,
  DEFAULT_CONTACT_PROPERTIES,
  DEFAULT_DEAL_PROPERTIES,
  DEFAULT_LEAD_PROPERTIES,
  DEFAULT_LINE_ITEM_PROPERTIES,
  DEFAULT_PRODUCT_PROPERTIES,
  DEFAULT_TICKET_PROPERTIES,
  SEARCHABLE_OBJECT_TYPES,
} from "../../common/constants.mjs";
import hubspot from "../../hubspot.app.mjs";
import common from "../common/common-create.mjs";
const DEFAULT_LIMIT = 200;
export default {
  key: "hubspot-search-crm",
  name: "Search CRM",
  description:
    "Search companies, contacts, deals, feedback submissions, products, tickets, line-items, quotes, leads, or custom objects. [See the documentation](https://developers.hubspot.com/docs/api/crm/search)",
  version: "1.1.2",
  annotations: {
    destructiveHint: false,
    openWorldHint: true,
    readOnlyHint: true,
  },
  type: "action",
  props: {
    hubspot,
    objectType: {
      type: "string",
      label: "Object Type",
      description: "Type of CRM object to search for",
      options: [
        ...SEARCHABLE_OBJECT_TYPES,
        {
          label: "Custom Object",
          value: "custom_object",
        },
      ],
      reloadProps: true,
    },
    exactMatch: {
      type: "boolean",
      label: "Exact Match",
      description:
        "Set to `true` to search for an exact match of the search value. If `false`, partial matches will be returned. Default: `true`",
      default: true,
      optional: true,
    },
    createIfNotFound: {
      type: "boolean",
      label: "Create if not found?",
      description:
        "Set to `true` to create the Hubspot object if it doesn't exist",
      default: false,
      optional: true,
      reloadProps: true,
    },
    offset: {
      type: "integer",
      label: "Offset",
      description: "The offset to start from. Used for pagination.",
      default: 0,
      optional: true,
    },
  },
  async additionalProps() {
    const props = {};
    if (this.objectType === "custom_object") {
      try {
        props.customObjectType = {
          type: "string",
          label: "Custom Object Type",
          options: async () => await this.getCustomObjectTypes(),
          reloadProps: true,
        };
      } catch {
        props.customObjectType = {
          type: "string",
          label: "Custom Object Type",
          reloadProps: true,
        };
      }
    }
    if (
      !this.objectType ||
      (this.objectType === "custom_object" && !this.customObjectType)
    ) {
      return props;
    }
    let schema;
    const objectType = this.customObjectType ?? this.objectType;
    try {
      schema = await this.hubspot.getSchema({
        objectType,
      });
      const properties = schema.properties;
      const searchableProperties = schema.searchableProperties?.map((prop) => {
        const propData = properties.find(({ name }) => name === prop);
        return {
          label: propData.label,
          value: propData.name,
        };
      });
      props.searchProperty = {
        type: "string",
        label: "Search Property",
        description: "The field to search",
        options: searchableProperties,
      };
    } catch {
      props.searchProperty = {
        type: "string",
        label: "Search Property",
        description: "The field to search",
      };
    }
    props.searchValue = {
      type: "string",
      label: "Search Value",
      description:
        "Search for objects where the specified search field/property contains a match of the search value",
    };
    const defaultProperties = this.getDefaultProperties();
    if (defaultProperties?.length) {
      props.info = {
        type: "alert",
        alertType: "info",
        content: `Properties:\n\`${defaultProperties.join(", ")}\``,
      };
    }
    try {
      
      props.additionalProperties = {
        type: "string[]",
        label: "Additional properties to retrieve",
        optional: true,
        options: async ({ page }) => {
          if (page !== 0) {
            return [];
          }
          const { results: properties } = await this.hubspot.getProperties({
            objectType: this.customObjectType ?? this.objectType,
          });
          const defaultProperties = this.getDefaultProperties();
          return properties
            .filter(({ name }) => !defaultProperties.includes(name))
            .map((property) => ({
              label: property.label,
              value: property.name,
            }));
        },
      };
    } catch {
      props.additionalProperties = {
        type: "string[]",
        label: "Additional properties to retrieve",
        optional: true,
      };
    }
    let creationProps = {};
    if (this.createIfNotFound && objectType) {
      try {
        const { results: properties } = await this.hubspot.getProperties({
          objectType,
        });
        const relevantProperties = properties.filter(this.isRelevantProperty);
        const propDefinitions = [];
        for (const property of relevantProperties) {
          propDefinitions.push(
            await this.makePropDefinition(property, schema.requiredProperties),
          );
        }
        creationProps = propDefinitions.reduce(
          (props, {
            name, ...definition
          }) => {
            props[name] = definition;
            return props;
          },
          {},
        );
      } catch {
        props.creationProps = {
          type: "object",
          label: "Object Properties",
          description:
            "A JSON object containing the object to create if not found",
        };
      }
    }
    return {
      ...props,
      ...creationProps,
    };
  },
  methods: {
    ...common.methods,
    getObjectType() {
      return this.objectType;
    },
    getDefaultProperties() {
      if (this.objectType === "contact") {
        return DEFAULT_CONTACT_PROPERTIES;
      } else if (this.objectType === "company") {
        return DEFAULT_COMPANY_PROPERTIES;
      } else if (this.objectType === "deal") {
        return DEFAULT_DEAL_PROPERTIES;
      } else if (this.objectType === "ticket") {
        return DEFAULT_TICKET_PROPERTIES;
      } else if (this.objectType === "product") {
        return DEFAULT_PRODUCT_PROPERTIES;
      } else if (this.objectType === "line_item") {
        return DEFAULT_LINE_ITEM_PROPERTIES;
      } else if (this.objectType === "lead") {
        return DEFAULT_LEAD_PROPERTIES;
      } else {
        return [];
      }
    },
    async getCustomObjectTypes() {
      const { results } = await this.hubspot.listSchemas();
      return (
        results?.map(({
          fullyQualifiedName: value, labels,
        }) => ({
          value,
          label: labels.plural,
        })) || []
      );
    },
  },
  async run({ $ }) {
    const {
      hubspot,
      objectType,
      customObjectType,
      additionalProperties = [],
      searchProperty,
      searchValue,
      exactMatch,
      offset,
      
      info,
      createIfNotFound,
      creationProps,
      ...otherProperties
    } = this;
    const actualObjectType = customObjectType ?? objectType;
    const schema = await this.hubspot.getSchema({
      objectType: actualObjectType,
    });
    if (!schema.searchableProperties.includes(searchProperty)) {
      throw new ConfigurationError(
        `Property \`${searchProperty}\` is not a searchable property of object type \`${objectType}\`. ` +
          `\n\nAvailable searchable properties are: \`${schema.searchableProperties.join("`, `")}\``,
      );
    }
    const properties = creationProps
      ? typeof creationProps === "string"
        ? JSON.parse(creationProps)
        : creationProps
      : otherProperties;
    const defaultProperties = this.getDefaultProperties();
    const data = {
      properties: [
        ...defaultProperties,
        ...additionalProperties,
        searchProperty,
      ],
      sorts: [
        {
          propertyName: "createdate",
          direction: "DESCENDING",
        },
      ],
      limit: DEFAULT_LIMIT,
      after: offset,
    };
    if (exactMatch) {
      data.filters = [
        {
          propertyName: searchProperty,
          operator: "EQ",
          value: searchValue,
        },
      ];
    }
    let {
      results, paging,
    } = await this.hubspot.searchCRM({
      object: actualObjectType,
      data,
    });
    if (!exactMatch) {
      results = results?.filter(
        (result) =>
          result.properties[searchProperty] &&
          result.properties[searchProperty]
            .toLowerCase()
            .includes(searchValue.toLowerCase()),
      );
    }
    if (!results?.length && createIfNotFound) {
      const response = await hubspot.createObject({
        $,
        objectType: actualObjectType,
        data: {
          properties,
        },
      });
      const objectName = hubspot.getObjectTypeName(actualObjectType);
      $.export("$summary", `Successfully created ${objectName}`);
      return response;
    }
    $.export(
      "$summary",
      `Successfully retrieved ${results?.length} object(s).`,
    );
    return {
      results,
      paging,
    };
  },
};