import { anySelectionValues } from "../constants/segments/attributes";

import { DateRangeType } from "../../BuilderPage/types/datepicker";
import {
  SegmentExpressionOperator,
  SegmentExpressionType,
  NestedSegmentStatement,
  SegmentAttribute,
  SegmentAttributeComparator,
  SegmentAttributeType,
  SegmentExpression,
  Segment,
  ContactType,
  MarketingPermission,
} from "@komi-app/fans-sdk";

import { v4 } from "uuid";

export const createDefaultSegment = (
  talentProfileId: string,
): Segment => ({
  segmentId: v4(),
  talentProfileId,
  name: "",
  contactType: ContactType.Any,
  marketingPermission: MarketingPermission.Any,
  expressions: [],
});
export const createDefaultExpression = (
  attributeType: SegmentAttributeType,
  expression?: Partial<SegmentExpression>,
): SegmentExpression => ({
  expressionId: v4(),
  ruleOrder: 0,
  expressionOrder: 0,
  expressionType: SegmentExpressionType.Clause,
  expressionOperator: SegmentExpressionOperator.And,
  attributeName: "",
  attributeType,
  attributeComparator: SegmentAttributeComparator.Eq,
  attributeValues: anySelectionValues,
  dateRangeEnd: 0,
  dateRangeStart: 0,
  dateRangeType: DateRangeType.All,
  inverted: false,
  ...expression,
});
export const createDefaultClause = (
  attributeType: SegmentAttributeType,
  expression?: Partial<SegmentExpression>,
): SegmentExpression => ({
  expressionId: v4(),
  ruleOrder: 0,
  expressionOrder: 0,
  expressionType: SegmentExpressionType.Clause,
  expressionOperator: SegmentExpressionOperator.And,
  attributeType,
  attributeName: "",
  attributeComparator: SegmentAttributeComparator.Eq,
  attributeValues: anySelectionValues,
  dateRangeEnd: 0,
  dateRangeStart: 0,
  dateRangeType: DateRangeType.All,
  inverted: false,
  ...expression,
});
export const createDefaultStatement = (
  attributeType: SegmentAttributeType,
  expression?: Partial<SegmentExpression>,
): SegmentExpression => ({
  expressionId: v4(),
  ruleOrder: 0,
  expressionOrder: 0,
  expressionType: SegmentExpressionType.Statement,
  expressionOperator: SegmentExpressionOperator.And,
  attributeType,
  attributeName: "",
  attributeComparator: SegmentAttributeComparator.Gte,
  attributeValues: ["1"],
  dateRangeEnd: 0,
  dateRangeStart: 0,
  dateRangeType: DateRangeType.All,
  inverted: false,
  ...expression,
});

/**
 * Order the expressions within a rule, according to their `expressionOrder` property
 * 
 * @param {SegmentRule} rule The rule whose expressions will be ordered
 * @returns {SegmentRule} The rule with ordered expressions
 */
export const orderExpressions = (expressions: SegmentExpression[]) => {
  return expressions.reduce<SegmentExpression[]>(
    (expressions, expression) => {
      expressions[expression.expressionOrder] = expression;
      return expressions;
    },
    [],
  );
};

/**
 * Sets the expressionOrder of expressions to their current array positions, offset
 * by an optional start order
 * 
 * @param {SegmentExpression[]} expressions 
 * @param {number} startOrder 
 * @returns {SegmentExpression[]}
 */
export const reorderExpressions = (expressions: SegmentExpression[], startOrder: number = 0) => {
  return expressions.map((expression, expressionIndex) => {
    const modifiedExpression: SegmentExpression = {
      ...expression,
      expressionOrder: expressionIndex + startOrder,
    };
    return modifiedExpression;
  });
};

/**
 * Flattens an array of nested statements into an array of statements and clauses, preserving the
 * input order of the nested statement expressions.
 * 
 * @param {NestedSegmentStatement[]} nestedStatements The nested statements to flatten
 * @returns {SegmentExpression[]} A flattened array of statements and their clauses, with retained order
 */
export const flattenNestedStatements = (nestedStatements: NestedSegmentStatement[]) => {
  return nestedStatements.reduce<SegmentExpression[]>(
    (expressions, nestedStatement) => {
      const { clauses, statementProperties, ...unnestedStatement } = nestedStatement;

      return [
        ...expressions,
        unnestedStatement,
        ...clauses,
      ];
    },
    [],
  );
};

/**
 * Unnest the statement from a nested statement, returning the statement and
 * the nested properties separately
 * 
 * @param {INestedSegmentStatement} nestedStatement The statement to unnest
 */
export const unnestStatement = (nestedStatement: NestedSegmentStatement): {
  clauses: SegmentExpression[],
  statementProperties: SegmentAttribute[],
  statement: SegmentExpression,
} => {
  const { clauses, statementProperties, ...statement } = nestedStatement;
  return { clauses, statementProperties, statement };
}
