import { type Type, applyDecorators } from '@nestjs/common';
import { ApiExtraModels, ApiOkResponse, ApiOperation, getSchemaPath } from '@nestjs/swagger';
import { OkResult } from '../dto/result.dto';

type ApiCustomOkResponseOP<T> = {
  /** 接口名称 */
  summary: string;

  /** 接口说明 */
  apiDescription?: string;

  /** 返回说明 */
  resDescription?: string;

  /** 默认值 */
  default?: unknown;

  /**
   * 接口实例
   * - 传入 DTO 类（如 TokenResponseDto）：使用该类作为 data 的类型
   * - 传入 null/undefined：data 为 null 类型
   * - 传入 Boolean/Number/String：使用对应的基本类型
   * - 传入 'boolean'/'number'/'string'：使用对应的基本类型（字符串形式)
   * - 传入 [Boolean]/[Number]/[String]：使用对应的数组类型
   * - 传入 ['boolean']/['number']/['string']：使用对应的数组类型（字符串形式）
   * - 传入 [TokenResponseDto]：使用 DTO 数组类型
   * - 传入 [null]：使用 null 数组类型
   */
  model: T;
};

/** DTO 类型 */
type DtoType = Type<unknown>;

/** 基本类型映射（构造函数 -> 字符串类型） */
const primitiveTypeMap = new Map<unknown, string>([
  [Boolean, 'boolean'],
  [Number, 'number'],
  [String, 'string'],
]);

/** 字符串基本类型集合（用于直接指定类型） */
const primitiveStringTypes = new Set(['boolean', 'number', 'string']);

/** Schema 类型定义 */
type SchemaType = { type: string; items?: { type: string; $ref?: string }; $ref?: string } | null;

/** 获取 schema 类型 */
function getSchemaType(model: unknown): SchemaType {
  // 处理 null 和 undefined
  if (model === null || model === undefined) {
    return { type: 'null' };
  }

  // 处理数组类型
  if (Array.isArray(model)) {
    if (model.length === 0) {
      return null;
    }
    const firstItem = model[0];
    // [null] 或 [undefined]
    if (firstItem === null || firstItem === undefined) {
      return { type: 'array', items: { type: 'null' } };
    }
    // ['boolean'], ['number'], ['string']
    if (typeof firstItem === 'string' && primitiveStringTypes.has(firstItem)) {
      return { type: 'array', items: { type: firstItem } };
    }
    // [Boolean], [Number], [String]
    const primitiveType = primitiveTypeMap.get(firstItem);
    if (primitiveType) {
      return {
        type: 'array',
        items: { type: primitiveType },
      };
    }

    // [TokenResponseDto], [BlacklistUserDto] 等 DTO 类数组
    if (typeof firstItem === 'function') {
      return {
        type: 'array',
        items: {
          type: 'object',
          $ref: getSchemaPath(firstItem as Type<unknown>),
        },
      };
    }
    return null;
  }

  // 'boolean', 'number', 'string'
  if (typeof model === 'string' && primitiveStringTypes.has(model)) {
    return { type: model };
  }

  // Boolean, Number, String
  const primitiveType = primitiveTypeMap.get(model);
  if (primitiveType) {
    return { type: primitiveType };
  }

  // TokenResponseDto, BlacklistUserDto 等 DTO 类 - 返回 null，让 else 分支处理
  if (typeof model === 'function') {
    return null;
  }

  return null;
}

/** 基本类型字符串 */
type PrimitiveString = 'boolean' | 'number' | 'string';

/** 基本类型构造函数 */
type PrimitiveCtor = BooleanConstructor | NumberConstructor | StringConstructor;

/** 自定义返回 */
export function ApiCustomOkResponse<T extends Array<DtoType | PrimitiveCtor | PrimitiveString | null> | DtoType | PrimitiveCtor | PrimitiveString | null>(
  op: ApiCustomOkResponseOP<T>
) {
  /** 额外需要导入的类型 */
  const extraModels: Type<unknown>[] = [OkResult];

  const arr = [
    ApiOperation({
      summary: op.summary,
      description: op.apiDescription,
    }),
  ];

  const schemaType = getSchemaType(op.model);

  if (schemaType) {
    // 基本类型或数组类型
    const isNullType = schemaType.type === 'null';
    const { items, ...restSchema } = schemaType;
    const dataProps: any = {
      ...restSchema,
      description: op.resDescription || '主体内容',
      default: op.default,
      example: isNullType ? null : undefined,
    };
    if (items) {
      dataProps.items = items;
    }
    arr.push(
      ApiOkResponse({
        schema: {
          description: op.resDescription || '主体内容',
          allOf: [
            { $ref: getSchemaPath(OkResult) },
            {
              properties: {
                data: dataProps,
              },
            },
          ],
        },
      })
    );
  } else if (Array.isArray(op.model)) {
    // DTO 数组类型 [TokenResponseDto]
    const itemModel = op.model[0];
    if (itemModel && typeof itemModel === 'function') {
      extraModels.push(itemModel);
      arr.push(
        ApiOkResponse({
          schema: {
            description: op.resDescription || '主体内容',
            allOf: [
              { $ref: getSchemaPath(OkResult) },
              {
                properties: {
                  data: {
                    type: 'array',
                    items: {
                      $ref: getSchemaPath(itemModel),
                    },
                    description: op.resDescription || '主体内容',
                    default: op.default,
                  },
                },
              },
            ],
          },
        })
      );
    }
  } else if (typeof op.model === 'function') {
    // 单个 DTO 类 TokenResponseDto
    extraModels.push(op.model as Type<unknown>);
    arr.push(
      ApiOkResponse({
        schema: {
          description: op.resDescription || '主体内容',
          allOf: [
            { $ref: getSchemaPath(OkResult) },
            {
              properties: {
                data: {
                  type: 'object',
                  $ref: getSchemaPath(op.model as Type<unknown>),
                },
              },
            },
          ],
        },
      })
    );
  }

  arr.push(ApiExtraModels(...extraModels));

  return applyDecorators(...arr);
}
