import type { CalcType, LinkProtocolType } from '@/types/my-kintone';
import { isType, type FieldType, type OneOf } from './kintone-rest-api-client';
import { KintoneFormFieldProperty } from '@kintone/rest-api-client';

export function isLeftJoinForceDisable(field: OneOf) {
  if (isType.CALC(field)) {
    return field.format === 'DAY_HOUR_MINUTE' || field.format === 'HOUR_MINUTE';
  }
  return false;
}

export function isRightJoinForceDisable(field: OneOf) {
  if (isLookup(field)) {
    return true;
  }
  return false;
}

export type SpecialType<T = string, F = any> = {
  type: T;
  format: F;
  check: (checkField: OneOf, selectedField?: OneOf) => boolean;
};

// LEFT
// LEFT - lookup
export type LookupTypeL2R = SpecialType<'LOOKUP_FROM_LEFT', FieldType[]>;
export const forMayLookup = (format: FieldType[]): LookupTypeL2R => {
  return {
    type: 'LOOKUP_FROM_LEFT',
    format,
    check: function (checkField: OneOf, selectedLeftField?: OneOf) {
      if (isLookup(checkField) && selectedLeftField) {
        return isLookup(selectedLeftField) ? checkField.type === selectedLeftField.type : false;
      }
      return !!this.format?.find((e) => e === checkField.type);
    },
  };
};

export const mayLookupText = forMayLookup(['SINGLE_LINE_TEXT']);
export const mayLookupNumber = forMayLookup(['NUMBER']);
export const mayLookupTextNumber = forMayLookup(['SINGLE_LINE_TEXT', 'NUMBER']);

// LEFT - calc
export type CalcTypeL2R = SpecialType<'CALC_FROM_LEFT', Record<CalcType, Array<FieldType | LookupTypeL2R>>>;

export const leftCalcType: CalcTypeL2R = {
  type: 'CALC_FROM_LEFT',
  format: {
    NUMBER: [mayLookupTextNumber],
    NUMBER_DIGIT: [mayLookupTextNumber],
    DATE: ['DATE'],
    TIME: ['TIME'],
    DATETIME: ['DATETIME'],
    HOUR_MINUTE: [],
    DAY_HOUR_MINUTE: [],
  },
  check: function (checkField: OneOf, selectedLeftField?: OneOf) {
    let allowed: Array<FieldType | LookupTypeL2R> = [];
    if (selectedLeftField && isType.CALC(selectedLeftField)) {
      allowed = this.format[selectedLeftField.format];
    }
    return !!allowed.find((e) => {
      if (isSpecialType(e) && isLookupFromLeft(e)) {
        return e.check(checkField, selectedLeftField);
      }
      return e === checkField.type;
    });
  },
};

// LEFT - link
export type LinkType = SpecialType<'LINK', Record<LinkProtocolType, LinkProtocolType[]>>;

export const linkType: LinkType = {
  type: 'LINK',
  format: {
    // 入力値の種別が同じ場合のみ
    WEB: ['WEB'],
    CALL: ['CALL'],
    MAIL: ['MAIL'],
  },
  check: function (checkField: OneOf, selectedField?: OneOf) {
    let allowed: LinkProtocolType[] = [];
    if (selectedField && isType.LINK(selectedField)) {
      allowed = this.format[selectedField.protocol];
    }
    if (checkField && isType.LINK(checkField)) {
      return !!allowed.find((e) => e === checkField.protocol);
    }
    return false;
  },
};

// LEFT - rule
export type AvailableRight = FieldType | CalcTypeL2R | LinkType | LookupTypeL2R;
const availableLeftJoinType = {
  SINGLE_LINE_TEXT: [mayLookupText],
  NUMBER: [mayLookupNumber],
  CALC: [leftCalcType],
  DATE: ['DATE'],
  TIME: ['TIME'],
  DATETIME: ['DATETIME'],
  LINK: [linkType],
} as Record<FieldType, AvailableRight[]>;

// RIGHT - calc
export type CalcTypeR2L = SpecialType<'CALC_FROM_RIGHT', CalcType[]>;

export const forCalc = (format: CalcType[]): CalcTypeR2L => {
  return {
    type: 'CALC_FROM_RIGHT',
    format,
    check: function (checkField: OneOf, selectedRightField?: OneOf) {
      return isType.CALC(checkField) && !!this.format?.find((e) => e === checkField.format);
    },
  };
};

// RIGHT - rule
export type AvailableLeft = FieldType | CalcTypeR2L | LinkType;
const availableRightJoinType = {
  SINGLE_LINE_TEXT: ['SINGLE_LINE_TEXT', forCalc(['NUMBER', 'NUMBER_DIGIT'])],
  NUMBER: ['NUMBER', forCalc(['NUMBER', 'NUMBER_DIGIT'])],
  DATE: ['DATE', forCalc(['DATE'])],
  TIME: ['TIME', forCalc(['TIME'])],
  DATETIME: ['DATETIME', forCalc(['DATETIME'])],
  LINK: [linkType],
} as Record<FieldType, AvailableLeft[]>;

// methods
// undefined means all
export function getRightAvailableJoinType(left?: OneOf | '') {
  if (left === undefined) {
    return Object.keys(availableRightJoinType) as FieldType[];
  }
  return left ? availableLeftJoinType[left.type] : [];
}

// undefined means all
export function getLeftAvailableJoinType(right?: OneOf | '') {
  if (right === undefined) {
    return Object.keys(availableLeftJoinType) as FieldType[];
  }
  return right ? availableRightJoinType[right.type] : [];
}

export function isSpecialType(obj: FieldType | SpecialType): obj is SpecialType {
  return typeof obj === 'object' && !Array.isArray(obj) && 'type' in obj;
}

export function isLookupFromLeft(obj: SpecialType): obj is LookupTypeL2R {
  return obj.type === 'LOOKUP_FROM_LEFT';
}

export function isLinkType(obj: SpecialType): obj is LinkType {
  return obj.type === 'LINK';
}

export function isCalcFromLeft(obj: SpecialType): obj is CalcTypeL2R {
  return obj.type === 'CALC_FROM_LEFT';
}

export function isCalcFromRight(obj: SpecialType): obj is CalcTypeR2L {
  return obj.type === 'CALC_FROM_RIGHT';
}

export function isLookup(field: OneOf): field is KintoneFormFieldProperty.Lookup {
  return 'lookup' in field;
}
