import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { MdmItemModelApi } from '#/api/mdm/item-model';

import { getItem as getMdmItem } from '#/api/mdm/item';
import { getItemModelListByItem } from '#/api/mdm/item-model';

/** 录入单位选项（含到物料主单位的换算比率） */
export interface ItemEnterUnitOption {
  id: number;
  name: string;
  rate: number;
}

/** 型号下拉选项：ApiSelect 会把 id/model 映射成 value/label，其余字段原样保留 */
export type ItemModelSelectOption = Pick<
  MdmItemModelApi.ItemModel,
  'unitMeasureId' | 'unitMeasureName' | 'unitMeasureRate'
>;

/** 出库/入库单行表单中与双单位换算相关的字段 */
export interface LineUnitFormValues {
  enterQuantity?: number;
  enterUnitMeasureId?: number;
  enterUnitMeasureName?: string;
  enterUnitRate?: number;
  itemId?: number;
  itemModelId?: number;
  itemPrimaryUnitId?: number;
  itemPrimaryUnitName?: string;
  quantityMax?: number;
}

/** 双单位换算用的临时表单字段（只供前端换算与展示，提交前必须剔除） */
const LINE_TEMP_FIELDS = [
  'itemPrimaryUnitId',
  'itemPrimaryUnitName',
  'enterUnitMeasureName',
  'enterUnitRate',
] as const;

/** 剔除双单位换算用的临时字段，得到可直接提交的行数据 */
export function omitLineTempFields<T extends object>(values: T): T {
  const data = { ...values } as Record<string, unknown>;
  for (const field of LINE_TEMP_FIELDS) {
    delete data[field];
  }
  return data as T;
}

/** 换算比率是否有效（必须大于 0） */
function isValidRate(rate?: number): rate is number {
  return typeof rate === 'number' && rate > 0;
}

/** 换算结果按 6 位小数展示（最终以后端换算结果为准） */
function roundQuantity(quantity: number): number {
  return Math.round(quantity * 1e6) / 1e6;
}

/**
 * 查询物料可选的录入单位：主单位 + 辅单位
 *
 * 选定型号时只保留「物料主单位」与「型号计量单位」两种口径，与后端换算规则保持一致
 */
export async function getItemEnterUnits(params: {
  itemId?: number;
  itemModelId?: number;
}): Promise<ItemEnterUnitOption[]> {
  const itemId = params.itemId;
  if (!itemId) {
    return [];
  }
  const item = await getMdmItem(itemId);
  if (!item?.unitMeasureId) {
    return [];
  }
  // 主单位始终可选，按主单位录入时不做换算
  const primaryUnit: ItemEnterUnitOption = {
    id: item.unitMeasureId,
    name: item.unitMeasureName || '物料主单位',
    rate: 1,
  };
  const unitOptions: ItemEnterUnitOption[] = [primaryUnit];
  const pushUnit = (id?: number, name?: string, rate?: number) => {
    if (
      !id ||
      id === primaryUnit.id ||
      !isValidRate(rate) ||
      unitOptions.some((unit) => unit.id === id)
    ) {
      return;
    }
    unitOptions.push({ id, name: name || '辅单位', rate });
  };
  if (params.itemModelId) {
    const models = await getItemModelListByItem(itemId);
    const modelOption = models?.find(
      (option) => option.id === params.itemModelId,
    );
    pushUnit(
      modelOption?.unitMeasureId,
      modelOption?.unitMeasureName,
      modelOption?.unitMeasureRate,
    );
    return unitOptions;
  }
  // 未选型号时，物料主数据上配置了换算比率的辅单位可选
  pushUnit(item.unitMeasureId2, item.unitMeasureName2, item.unitMeasureRate1);
  pushUnit(item.unitMeasureId3, item.unitMeasureName3, item.unitMeasureRate2);
  return unitOptions;
}

/** 查询某个录入单位到物料主单位的换算比率（编辑回显时补齐前端换算上下文） */
export async function getEnterUnitRate(
  itemId?: number,
  itemModelId?: number,
  enterUnitMeasureId?: number,
): Promise<number | undefined> {
  if (!itemId || !enterUnitMeasureId) {
    return undefined;
  }
  const unitOptions = await getItemEnterUnits({ itemId, itemModelId });
  return unitOptions.find((unit) => unit.id === enterUnitMeasureId)?.rate;
}

/** 行表单双单位录入的联动方法 */
export interface LineUnitHandlers {
  /** 是否按物料主单位录入（未选录入单位、或录入单位就是物料主单位） */
  isPrimaryUnitEnter: (values: LineUnitFormValues) => boolean;
  /** 切换物料：重新记录物料主单位，并清空型号、录入单位与数量（口径都随物料变化） */
  handleItemChange: (itemId?: number) => Promise<void>;
  /** 选择型号：默认按型号计量单位录入，并记录换算比率 */
  handleModelChange: (
    option: ItemModelSelectOption | undefined,
    values: LineUnitFormValues,
  ) => Promise<void>;
  /** 切换录入单位：记录换算比率并立即重算主单位数量；改回主单位时清掉录入数量 */
  handleEnterUnitChange: (
    value: number | undefined,
    option: { label?: string; rate?: number } | undefined,
    values: LineUnitFormValues,
  ) => Promise<void>;
  /** 录入数量变化：按录入单位与换算比率试算主单位数量，供用户核对换算结果 */
  handleEnterQuantityChange: (
    value: number | undefined,
    values: LineUnitFormValues,
  ) => Promise<void>;
}

/**
 * 生成行表单的双单位录入联动方法
 *
 * @param quantityField 物料主单位口径的数量字段名（出库为 quantity，入库为 receivedQuantity）
 */
export function useLineUnitHandlers(
  formApi?: VbenFormApi,
  quantityField = 'quantity',
): LineUnitHandlers {
  function isPrimaryUnitEnter(values: LineUnitFormValues) {
    return (
      !values.enterUnitMeasureId ||
      values.enterUnitMeasureId === values.itemPrimaryUnitId
    );
  }

  async function handleItemChange(itemId?: number) {
    // 先清空，避免上一个物料的型号与单位口径残留
    await formApi?.setValues({
      itemId,
      itemModelId: undefined,
      itemPrimaryUnitId: undefined,
      itemPrimaryUnitName: undefined,
      enterUnitMeasureId: undefined,
      enterUnitMeasureName: undefined,
      enterUnitRate: undefined,
      enterQuantity: undefined,
      [quantityField]: undefined,
    });
    if (!itemId) {
      return;
    }
    const item = await getMdmItem(itemId);
    await formApi?.setValues({
      itemPrimaryUnitId: item?.unitMeasureId,
      itemPrimaryUnitName: item?.unitMeasureName,
    });
  }

  async function handleModelChange(
    option: ItemModelSelectOption | undefined,
    values: LineUnitFormValues,
  ) {
    const enterUnitMeasureId =
      option?.unitMeasureId && option.unitMeasureId !== values.itemPrimaryUnitId
        ? option.unitMeasureId
        : undefined;
    await formApi?.setValues({
      enterUnitMeasureId,
      enterUnitMeasureName: enterUnitMeasureId
        ? option?.unitMeasureName
        : undefined,
      enterUnitRate: enterUnitMeasureId ? option?.unitMeasureRate : undefined,
      enterQuantity: undefined,
      // 切到型号单位录入后主单位数量由系统换算，先清掉上一次口径的数值
      ...(enterUnitMeasureId ? { [quantityField]: undefined } : {}),
    });
  }

  async function handleEnterUnitChange(
    value: number | undefined,
    option: { label?: string; rate?: number } | undefined,
    values: LineUnitFormValues,
  ) {
    if (!value || value === values.itemPrimaryUnitId) {
      await formApi?.setValues({
        enterUnitMeasureName: undefined,
        enterUnitRate: undefined,
        enterQuantity: undefined,
      });
      return;
    }
    await formApi?.setValues({
      enterUnitMeasureName: option?.label,
      enterUnitRate: option?.rate,
      ...(values.enterQuantity == null || !isValidRate(option?.rate)
        ? { [quantityField]: undefined }
        : {
            [quantityField]: roundQuantity(
              values.enterQuantity * option.rate,
            ),
          }),
    });
  }

  async function handleEnterQuantityChange(
    value: number | undefined,
    values: LineUnitFormValues,
  ) {
    if (isPrimaryUnitEnter(values) || !isValidRate(values.enterUnitRate)) {
      return;
    }
    await formApi?.setFieldValue(
      quantityField,
      value == null ? undefined : roundQuantity(value * values.enterUnitRate),
    );
  }

  return {
    handleEnterQuantityChange,
    handleEnterUnitChange,
    handleItemChange,
    handleModelChange,
    isPrimaryUnitEnter,
  };
}

/** 行表单「物料型号 / 录入单位 / 录入数量 / 主单位数量」相关字段的通用配置 */
export interface ItemUnitSchemaOptions {
  /** 物料主单位口径的数量字段名（默认 quantity） */
  quantityField?: string;
  /** 物料主单位口径的数量字段标签（默认「出库数量」） */
  quantityLabel?: string;
}

/**
 * 生成行表单的双单位字段组（型号、录入单位、录入数量、主单位数量及换算用隐藏字段）
 *
 * 主单位数量字段在按辅单位录入时由系统换算、不可手工修改；按主单位录入时手工填写并受库存上限约束
 */
export function buildItemUnitSchema(
  formApi?: VbenFormApi,
  options: ItemUnitSchemaOptions = {},
): VbenFormSchema[] {
  const quantityField = options.quantityField ?? 'quantity';
  const quantityLabel = options.quantityLabel ?? '出库数量';
  const handlers = useLineUnitHandlers(formApi, quantityField);

  return [
    // 物料主单位随所选物料变化，但表单 props 只能静态声明，故用隐藏字段承载
    {
      fieldName: 'itemPrimaryUnitId',
      component: 'Input',
      dependencies: {
        triggerFields: [''],
        show: () => false,
      },
    },
    {
      fieldName: 'itemPrimaryUnitName',
      component: 'Input',
      dependencies: {
        triggerFields: [''],
        show: () => false,
      },
    },
    {
      fieldName: 'itemModelId',
      label: '物料型号',
      component: 'ApiSelect',
      componentProps: {
        labelField: 'model',
        valueField: 'id',
        allowClear: true,
        api: (params: { itemId?: number }) =>
          getItemModelListByItem(params.itemId!),
        shouldFetch: (params: { itemId?: number }) => !!params?.itemId,
        placeholder: '请先选择物料',
      },
      dependencies: {
        triggerFields: ['itemId'],
        componentProps: (values) => ({
          disabled: !values.itemId,
          params: { itemId: values.itemId },
          placeholder: values.itemId ? '请选择物料型号' : '请先选择物料',
          // 选定型号后默认按其计量单位录入
          onChange: (option?: ItemModelSelectOption) =>
            handlers.handleModelChange(option, values),
        }),
      },
    },
    {
      fieldName: 'enterUnitMeasureId',
      label: '录入单位',
      component: 'ApiSelect',
      componentProps: {
        allowClear: true,
        labelField: 'name',
        valueField: 'id',
        api: (params: { itemId?: number; itemModelId?: number }) =>
          getItemEnterUnits(params),
        shouldFetch: (params: { itemId?: number }) => !!params?.itemId,
        placeholder: '默认为物料主计量单位',
      },
      dependencies: {
        triggerFields: ['itemId', 'itemModelId', 'itemPrimaryUnitName'],
        componentProps: (values) => ({
          disabled: !values.itemId,
          params: { itemId: values.itemId, itemModelId: values.itemModelId },
          placeholder: values.itemId
            ? `默认按物料主单位${values.itemPrimaryUnitName ? `【${values.itemPrimaryUnitName}】` : ''}录入`
            : '请先选择物料',
          onChange: (
            value: number | undefined,
            option?: { label?: string; rate?: number },
          ) => handlers.handleEnterUnitChange(value, option, values),
        }),
      },
    },
    // 录入单位名称与换算比率随所选单位变化，同样用隐藏字段承载
    {
      fieldName: 'enterUnitMeasureName',
      component: 'Input',
      dependencies: {
        triggerFields: [''],
        show: () => false,
      },
    },
    {
      fieldName: 'enterUnitRate',
      component: 'Input',
      dependencies: {
        triggerFields: [''],
        show: () => false,
      },
    },
    {
      fieldName: 'enterQuantity',
      label: '录入数量',
      component: 'InputNumber',
      componentProps: {
        class: '!w-full',
        min: 0,
        precision: 6,
      },
      dependencies: {
        triggerFields: [
          'enterUnitMeasureId',
          'enterUnitMeasureName',
          'itemPrimaryUnitId',
          'quantityMax',
          'enterUnitRate',
        ],
        show: (values) => !handlers.isPrimaryUnitEnter(values),
        // 返回 null 表示不追加规则（回退基础 rules）
        rules: (values) =>
          handlers.isPrimaryUnitEnter(values) ? null : 'required',
        componentProps: (values) => ({
          placeholder: values.enterUnitMeasureName
            ? `请填写按【${values.enterUnitMeasureName}】计的数量`
            : '请按录入单位填写数量',
          // 按辅单位录入时，可录入上限为「库存上限 ÷ 换算比率」
          max:
            values.quantityMax != null && isValidRate(values.enterUnitRate)
              ? Math.floor((values.quantityMax / values.enterUnitRate) * 1e6) /
                1e6
              : undefined,
          // 录入数量变化时立即试算主单位数量
          onChange: (value: number | undefined) =>
            handlers.handleEnterQuantityChange(value, values),
        }),
      },
    },
    {
      fieldName: quantityField,
      label: quantityLabel,
      component: 'InputNumber',
      componentProps: {
        class: '!w-full',
        min: 0.01,
        precision: 6,
      },
      dependencies: {
        triggerFields: [
          'enterUnitMeasureId',
          'itemPrimaryUnitId',
          'itemPrimaryUnitName',
          'quantityMax',
        ],
        // 按主单位录入时手工填写、必填；按辅单位录入时为系统换算结果，无需校验
        rules: (values) =>
          handlers.isPrimaryUnitEnter(values) ? 'required' : null,
        componentProps: (values) => {
          const primary = handlers.isPrimaryUnitEnter(values);
          return {
            disabled: !primary,
            max: values.quantityMax,
            placeholder: primary
              ? `请按物料主单位${values.itemPrimaryUnitName ? `【${values.itemPrimaryUnitName}】` : ''}填写数量`
              : '按录入单位与换算比率自动换算',
          };
        },
      },
    },
  ];
}
