import 'dart:convert';

import 'package:code_builder/code_builder.dart';
import 'package:swagger_dart_code_generator/src/models/generator_options.dart';
import 'package:swagger_dart_code_generator/src/code_generators/swagger_models_generator.dart';
import 'package:swagger_dart_code_generator/src/extensions/string_extension.dart';
import 'package:swagger_dart_code_generator/src/swagger_models/requests/swagger_request.dart';
import 'package:swagger_dart_code_generator/src/swagger_models/requests/swagger_request_parameter.dart';
import 'package:swagger_dart_code_generator/src/swagger_models/responses/swagger_response.dart';
import 'package:swagger_dart_code_generator/src/swagger_models/responses/swagger_schema.dart';
import 'package:swagger_dart_code_generator/src/swagger_models/swagger_path.dart';
import 'package:swagger_dart_code_generator/src/swagger_models/swagger_root.dart';
import 'package:recase/recase.dart';
import 'package:collection/collection.dart';
import 'package:swagger_dart_code_generator/src/extensions/parameter_extensions.dart';

import 'constants.dart';

class SwaggerRequestsGenerator {
  String generate({
    required String code,
    required String className,
    required String fileName,
    required GeneratorOptions options,
    required Map<String, dynamic> requestClass,
  }) {
    final map = jsonDecode(code) as Map<String, dynamic>;
    final swaggerRoot = SwaggerRoot.fromJson(map);

    swaggerRoot.paths = _processVersion(swaggerRoot, options);

    final service = _generateService(swaggerRoot, className, fileName, options, requestClass);

    return service.accept(DartEmitter()).toString();
  }

  Map<String, SwaggerPath> _processVersion(SwaggerRoot content, GeneratorOptions options) {
    var returnValues = <String, SwaggerPath>{};

    // ignore: unused_local_variable
    content.paths.forEach((key, value) {
      if (options.urlParterns.isEmpty ||
          options.urlParterns.where((element) => key.toLowerCase().contains(element.toLowerCase())).isNotEmpty) {
        if (options.customApiVersion.isNotEmpty) {
          options.customApiVersion.forEach((element) {
            for (var temp in element.urls) {
              if (key.toLowerCase() == temp.toLowerCase()) {
                key = key.replaceAll('{version}', element.version);
              }
            }
          });
        }
        //else {
        if (key.contains('{version}')) key = key.replaceAll('{version}', options.defaultApiVersion);
        //}
        returnValues[key] = value;
      }
    });
    return returnValues;
  }

  Class _generateService(
    SwaggerRoot swaggerRoot,
    String className,
    String fileName,
    GeneratorOptions options,
    Map<String, dynamic> requestClass,
  ) {
    final allMethodsContent = _getAllMethodsContent(
      swaggerRoot: swaggerRoot,
      options: options,
      requestClass: requestClass,
    );

    // final chopperClient = SwaggerAdditionsGenerator.getChopperClientContent(
    //   className,
    //   swaggerRoot.host,
    //   swaggerRoot.basePath,
    //   options,
    // );

    var constructor = Constructor((p) => p
      ..factory = true
      ..redirect = Reference('_$className')
      ..requiredParameters.add(
        Parameter((p) => p
          ..type = Reference('Dio')
          ..name = 'dio'),
      )
      ..optionalParameters.add(
        Parameter((p) => p
          ..type = Reference('String')
          ..name = 'baseUrl'
          ..named = true),
      ));

    return Class(
      (c) => c
        ..methods.addAll([...allMethodsContent])
        ..constructors.add(constructor)
        //..extend = Reference(kChopperService)
        ..docs.add(kServiceHeader)
        ..annotations.add(refer(kRetrofitApi).call(
          [],
        )) //{'baseUrl': Reference('ApiPath.EXAM')}
        ..abstract = true
        ..name = className,
    );
  }

  List<Method> _getAllMethodsContent({
    required SwaggerRoot swaggerRoot,
    required GeneratorOptions options,
    required Map<String, dynamic> requestClass,
  }) {
    final methods = <Method>[];

    swaggerRoot.paths.forEach((String path, SwaggerPath swaggerPath) {
      swaggerPath.requests.forEach((String requestType, SwaggerRequest swaggerRequest) {
        if (requestType.toLowerCase() == kRequestTypeOptions) {
          return;
        }

        final item = options.excludePaths
            .firstWhereOrNull((excludePath) => excludePath.startsWith(path)); //RegExp(excludePath).hasMatch(path)
        print(path);
        if (item != null) {
          if (item.contains(' ')) {
            final temps = item.split(' ');
            print("Method: " + temps[temps.length - 1].toLowerCase());
            print("Method 2: " + requestType.toLowerCase());
            if (temps[temps.length - 1].toLowerCase() == requestType.toLowerCase()) return;
          } else {
            return;
          }
        }

        if (options.includePaths.isNotEmpty &&
            !options.includePaths.any((includePath) => RegExp(includePath).hasMatch(path))) {
          return;
        }

        final methodName = _getRequestMethodName(
            requestType: requestType, swaggerRequest: swaggerRequest, path: path, options: options);

        // Nếu đã có method trùng tên (thường là do khác HTTP method trên cùng path)
        // thì thêm prefix theo method (get/put/post/delete...) để tránh trùng,
        // ví dụ: GET /order/{id} -> orderId, PUT /order/{id} -> putOrderId.
        var effectiveMethodName = methodName;
        if (methods.any((m) => m.name == methodName)) {
          effectiveMethodName = '${requestType.toLowerCase()}${methodName.capitalize}';
        }

        final parameters = _getAllParameters(
          swaggerRequest: swaggerRequest,
          ignoreHeaders: options.ignoreHeaders,
          modelPostfix: options.modelPostfix,
          swaggerPath: swaggerPath,
          path: path,
          requestType: requestType,
          root: swaggerRoot,
          options: options,
        );

        // Nếu cấu hình override multipart cho url + method này,
        // force signature dùng @Part() Map<String, dynamic> body.
        final hasMultipartOverride = options.requestOverrideValueMap.any(
          (override) =>
              override.url == path &&
              override.method.toLowerCase() == requestType.toLowerCase() &&
              override.paramName.toLowerCase() == 'multipart',
        );

        if (hasMultipartOverride) {
          parameters
            ..clear()
            ..add(
              Parameter(
                (p) => p
                  ..name = kBody
                  ..named = true
                  ..type = Reference('Map<String, dynamic>')
                  ..annotations.add(
                    refer('Part').call([]),
                  ),
              ),
            );
        }

        // Override request params
        final temp = options.requestOverrideValueMap
            .firstWhereOrNull((element) => element.url == path && element.method == 'get');
        // print(path);
        if (temp != null) {
          var name = temp.overriddenValue;
          parameters.clear();
          parameters.add(
            Parameter((p) => p
              ..name = name.camelCase
              ..named = true
              ..type = Reference(name.capitalize)
              ..annotations.add(
                refer('Queries').call([]),
              )
              ..named = true),
          );
        } else if (parameters.length > 2) {
          var temps = <String, dynamic>{};
          for (var item in _getParameter(swaggerRequest.parameters, options.ignoreHeaders, options.includePaths)) {
            if (item.schema != null) {
              var instance = item.schema!;
              temps[item.name] = {'type': instance.type, 'required': item.isRequired};
            }
          }
          var name = effectiveMethodName + 'Request';
          requestClass[name] = {'type': 'object', 'properties': temps};

          parameters.clear();
          parameters.add(
            Parameter((p) => p
              ..name = name.camelCase
              ..named = true
              ..type = Reference(name.capitalize)
              ..annotations.add(
                refer('Queries').call([]),
              )
              ..named = true),
          );
        }

        final returnTypeName = _getReturnTypeName(
          responses: swaggerRequest.responses,
          path: path,
          methodName: effectiveMethodName,
          modelPostfix: options.modelPostfix,
          swaggerRoot: swaggerRoot,
          overridenResponses: options.responseOverrideValueMap.asMap().map((key, value) => MapEntry(value.url, value)),
        );

        final returns = returnTypeName.isEmpty ? kFutureResponse : returnTypeName.asFutureResponse();

        final hasOptionalBody = ['post', 'put', 'patch'].contains(requestType) &&
            swaggerRequest.parameters.none((p) => p.inParameter == kBody) &&
            swaggerRequest.requestBody == null;

        final method = Method((m) => m
          ..requiredParameters.addAll(parameters)
          ..docs.add(_getCommentsForMethod(
            methodDescription: swaggerRequest.summary,
            parameters: swaggerRequest.parameters,
            options: options,
          ))
          ..name = effectiveMethodName
          ..annotations.addAll(
            _getMethodAnnotations(requestType, path, hasOptionalBody, options),
          )
          ..returns = Reference(returns));

        if (_hasEnumProperties(method)) {
          final privateMethod = _getPrivateMethod(method);
          final publicMethod = _getPublicMethod(method);
          methods.addAll([publicMethod, privateMethod]);
        } else {
          methods.add(method);
        }
      });
    });

    return methods;
  }

  bool _hasEnumProperties(Method method) {
    return method.optionalParameters.any((p) => p.type!.symbol!.startsWith('enums') == true);
  }

  Method _getPrivateMethod(Method method) {
    final parameters = method.optionalParameters.map((p) {
      if (p.type!.symbol!.startsWith('enums.')) {
        return p.copyWith(type: Reference('String?'));
      }

      return p;
    });

    return Method(
      (m) => m
        ..optionalParameters.addAll(parameters)
        ..docs.addAll(method.docs)
        ..name = '_${method.name}'
        ..annotations.addAll(method.annotations)
        ..returns = method.returns,
    );
  }

  Method _getPublicMethod(Method method) {
    final parameters = method.optionalParameters.map((p) => p.copyWith(annotations: []));

    return Method(
      (m) => m
        ..optionalParameters.addAll(parameters)
        ..docs.addAll(method.docs)
        ..name = method.name
        ..returns = method.returns
        ..body = _generatePublicMethodReturn(parameters, method.name!),
    );
  }

  Code _generatePublicMethodReturn(Iterable<Parameter> parameters, String publicMethodName) {
    final parametersListString = parameters.map((p) {
      if (p.type!.symbol!.startsWith('enums.')) {
        final enumName = p.type!.symbol!.replaceFirst('enums.', '').replaceAll('?', '');

        return '${p.name} : enums.\$${enumName}Map[${p.name}]';
      }
      return '${p.name} : ${p.name}';
    }).join(', ');

    return Code('return _$publicMethodName($parametersListString);');
  }

  /// Trả về danh sách annotation cho method (ví dụ: @POST + @MultiPart).
  List<Expression> _getMethodAnnotations(
    String requestType,
    String path,
    bool hasOptionalBody,
    GeneratorOptions options,
  ) {
    final annotations = <Expression>[];

    // Annotation HTTP method chính (@GET/@POST/...)
    annotations.add(
      refer(requestType.pascalCase.toUpperCase()).call(
        [literalString(path)],
        //{kPath: literalString(path), if (hasOptionalBody) 'optionalBody': refer(true.toString())},
        {},
      ),
    );

    // Nếu có override kiểu "multipart" cho url + method này thì thêm @MultiPart()
    final isMultipart = options.requestOverrideValueMap.any(
      (override) =>
          override.url == path &&
          override.method.toLowerCase() == requestType.toLowerCase() &&
          override.paramName.toLowerCase() == 'multipart',
    );

    if (isMultipart) {
      annotations.add(refer('MultiPart').call([]));
    }

    return annotations;
  }

  String _getCommentsForMethod({
    required String methodDescription,
    required List<SwaggerRequestParameter> parameters,
    required GeneratorOptions options,
  }) {
    final parametersComments = parameters.map((SwaggerRequestParameter parameter) => _createSummaryParameters(
          parameter.name,
          parameter.description,
          parameter.inParameter,
          options,
        ));

    final formattedDescription = methodDescription.split('\n').join('\n///');

    return ['///$formattedDescription', ...parametersComments].where((String element) => element.isNotEmpty).join('\n');
  }

  String _createSummaryParameters(
      String parameterName, String parameterDescription, String inParameter, GeneratorOptions options) {
    if (inParameter == kHeader && options.ignoreHeaders) {
      return '';
    }
    if (parameterDescription.isNotEmpty) {
      parameterDescription = parameterDescription.replaceAll(RegExp(r'\n|\r|\t'), ' ');
    } else {
      parameterDescription = '';
    }

    return '///@param $parameterName $parameterDescription';
  }

  Expression _getParameterAnnotation(SwaggerRequestParameter parameter) {
    switch (parameter.inParameter) {
      case kFormData:
        return refer(kField).call([literalString(parameter.name.replaceAll('\$', ''))]);
      case kBody:
        return refer(kBody.pascalCase).call([]);
      default:
        //https://github.com/lejard-h/chopper/issues/295
        return refer(parameter.inParameter.pascalCase).call([literalString(parameter.name.replaceAll('\$', ''))]);
    }
  }

  String _getEnumParameterTypeName({
    required String parameterName,
    required String path,
    required String requestType,
  }) {
    final pathString = path.split('/').map((e) => e.replaceAll('}', '').replaceAll('{', '').pascalCase).join();

    final result = '$pathString${requestType.pascalCase}${SwaggerModelsGenerator.getValidatedClassName(parameterName)}';

    return result.asEnum();
  }

  bool _isEnumRefParameter(SwaggerRequestParameter parameter, SwaggerRoot root) {
    final schemas = root.components?.schemas ?? {};
    schemas.addAll(root.definitions);

    final refs = [
      parameter.schema?.items?.ref.getRef(),
      parameter.schema?.ref.getRef(),
      parameter.items?.ref.getRef(),
    ];
    final schema = schemas[refs.firstWhereOrNull((ref) => ref?.isNotEmpty == true)];

    if (schema == null) {
      return false;
    }

    if (schema.type == kString && schema.enumValues.isNotEmpty) {
      return true;
    }

    return false;
  }

  String _getParameterTypeName({
    required SwaggerRequestParameter parameter,
    required String path,
    required String requestType,
    required String modelPostfix,
    required SwaggerRoot root,
    required List<RequestOverrideValueMap> overridenRequests,
  }) {
    if (parameter.name == 'gender') {
      // print(parameter.toJson());
      // print(parameter.schema?.toJson());
    }
    final temp =
        overridenRequests.firstWhereOrNull((element) => element.url == path && element.paramName == parameter.name);
    if (temp != null) {
      return temp.overriddenValue;
    }
    if (parameter.inParameter == kHeader) {
      return _mapParameterName(kString, '');
    } else if (parameter.items?.enumValues.isNotEmpty == true || parameter.schema?.enumValues.isNotEmpty == true) {
      return _getEnumParameterTypeName(parameterName: parameter.name, path: path, requestType: requestType);
    } else if (parameter.items?.type.isNotEmpty == true) {
      return _mapParameterName(parameter.items!.type, modelPostfix).asList();
    } else if (parameter.items?.ref.isNotEmpty == true) {
      if (_isEnumRefParameter(parameter, root)) {
        return parameter.items!.ref.getRef().asEnum();
      }
      return _mapParameterName(parameter.items!.ref.getRef(), modelPostfix).asList();
    } else if (parameter.schema?.items?.ref.isNotEmpty == true) {
      if (_isEnumRefParameter(parameter, root)) {
        return parameter.schema!.items!.ref.getRef().asEnum();
      }
      return (parameter.schema!.items!.ref.getRef() + modelPostfix).asList();
    } else if (parameter.schema?.ref.isNotEmpty == true) {
      if (parameter.schema!.ref.toLowerCase().contains('enum')) {
        return 'int';
      }
      if (parameter.schema!.ref.toLowerCase().contains('short')) {
        return 'int';
      }
      if (_isEnumRefParameter(parameter, root)) {
        return parameter.schema!.ref.getRef().asEnum();
      }

      if (parameter.schema!.items != null || parameter.schema!.type == kArray) {
        return (parameter.schema!.ref.getRef() + modelPostfix).asList();
      }
      return (parameter.schema!.ref.getRef() + modelPostfix);
    } else if (parameter.schema?.type == kArray && parameter.schema?.items?.type.isNotEmpty == true) {
      return _mapParameterName(parameter.schema!.items!.type, '').asList();
    } else if (parameter.schema?.anyOf.firstOrNull?.type.isNotEmpty == true) {
      return _mapParameterName(parameter.schema!.anyOf.first.type, '');
    }

    final neededType = parameter.type.isNotEmpty ? parameter.type : parameter.schema?.type ?? kObject.pascalCase;

    return _mapParameterName(neededType, modelPostfix);
  }

  String _mapParameterName(String name, String modelPostfix) {
    return kBasicTypesMap[name] ?? name.pascalCase + modelPostfix;
  }

  List<SwaggerRequestParameter> _getParameter(
      List<SwaggerRequestParameter> parameters, bool ignoreHeaders, List<String> excludeParams) {
    return parameters
        .where((element) => excludeParams.isEmpty || !excludeParams.contains(element.name.toLowerCase()))
        .where((swaggerParameter) => ignoreHeaders ? swaggerParameter.inParameter != kHeader : true)
        .where((swaggerParameter) => swaggerParameter.inParameter != kCookie)
        .where((swaggerParameter) => swaggerParameter.inParameter.isNotEmpty)
        .toList();
  }

  List<Parameter> _getAllParameters({
    required SwaggerRequest swaggerRequest,
    required bool ignoreHeaders,
    required String path,
    required String requestType,
    required String modelPostfix,
    required SwaggerRoot root,
    required GeneratorOptions options,
    required SwaggerPath swaggerPath,
  }) {
    final definedParameters = <String, SwaggerRequestParameter>{};
    definedParameters.addAll(root.parameters);
    definedParameters.addAll(root.components?.parameters ?? {});

    var excludeParams = options.excludeParams.map((e) => e.toLowerCase()).toList();

    final parameters = [...swaggerRequest.parameters, ...swaggerPath.parameters]
        .map((par) => definedParameters[par.ref.split('/').last] ?? par)
        .toList();

    final overridenRequests = options.requestOverrideValueMap;

    final isMultipartOverride = overridenRequests.any(
      (override) =>
          override.url == path &&
          override.method.toLowerCase() == requestType.toLowerCase() &&
          override.paramName.toLowerCase() == 'multipart',
    );

    final result = _getParameter(parameters, ignoreHeaders, excludeParams)
        // .where((element) => excludeParams.isEmpty || !excludeParams.contains(element.name.toLowerCase()))
        // .where((swaggerParameter) => ignoreHeaders ? swaggerParameter.inParameter != kHeader : true)
        // .where((swaggerParameter) => swaggerParameter.inParameter != kCookie)
        // .where((swaggerParameter) => swaggerParameter.inParameter.isNotEmpty)
        .map(
          (swaggerParameter) => Parameter(
            (p) => p
              ..name = swaggerParameter.name.asParameterName()
              ..named = true
              ..required = false
              ..type = Reference(
                _getParameterTypeName(
                  parameter: swaggerParameter,
                  path: path,
                  requestType: requestType,
                  modelPostfix: modelPostfix,
                  root: root,
                  overridenRequests: overridenRequests,
                ), //.makeNullable(),
              )
              ..named = true
              ..annotations.add(
                _getParameterAnnotation(swaggerParameter),
              )
              ..defaultTo = _getHeaderDefaultValue(swaggerParameter, options),
          ),
        )
        .toList();

    final requestBody = swaggerRequest.requestBody;

    if (requestBody != null) {
      var typeName = '';

      final temp = overridenRequests.firstWhereOrNull((element) => element.url == path && element.paramName == 'body');
      if (temp != null) {
        typeName = temp.overriddenValue;
      } else {
        final ref = requestBody.ref;
        if (ref.isNotEmpty) {
          typeName = ref.getRef();

          final requestBodyRef = root.components?.requestBodies[ref.getRef()]?.ref ?? '';

          if (requestBodyRef.isNotEmpty == true) {
            typeName = requestBodyRef.getRef();
          }

          typeName = SwaggerModelsGenerator.getValidatedClassName(typeName);
        }

        final schema = requestBody.content?.schema;

        if (schema != null) {
          // Nếu endpoint được đánh dấu multipart trong request_override_value_map,
          // sinh một param duy nhất dạng @Part() Map<String, dynamic> body
          // để caller tự build map/form-data.
          if (isMultipartOverride) {
            result.add(
              Parameter(
                (p) => p
                  ..name = kBody
                  ..named = true
                  ..type = Reference('Map<String, dynamic>')
                  ..annotations.add(
                    refer('Part').call([]),
                  ),
              ),
            );

            // Không thêm @Body nữa.
            return result.distinctParameters();
          }

          if (schema.format == kBinary) {
            typeName = kObject.pascalCase;
          } else {
            typeName = _getRequestBodyTypeName(
              schema: schema,
              modelPostfix: options.modelPostfix,
              root: root,
            );
          }
        }
      }

      if (typeName.isNotEmpty) {
        result.add(
          Parameter(
            (p) => p
              ..name = kBody
              ..named = true
              //..required = true
              ..type = Reference(typeName //.makeNullable(),
                  )
              ..named = true
              ..annotations.add(
                refer(kBody.pascalCase).call([]),
              ),
          ),
        );
      }
    }

    return result.distinctParameters();
  }

  bool _isBasicTypeRef(String ref, SwaggerRoot root) {
    final schemas = _getAllReusableObjects(root);

    final neededSchema = schemas[ref.getUnformattedRef()];

    if (neededSchema == null) {
      return false;
    }

    return kBasicTypes.contains(neededSchema.type);
  }

  bool _isEnumRef(String ref, SwaggerRoot root) {
    final schemas = root.components?.schemas ?? <String, SwaggerSchema>{};
    schemas.addAll(root.definitions);

    final neededSchemaKey = schemas.keys.firstWhereOrNull((key) => key.getRef() == ref.getRef());

    if (neededSchemaKey == null) {
      return false;
    }

    final neededSchema = schemas[neededSchemaKey]!;

    if (neededSchema.type == kString && neededSchema.enumValues.isNotEmpty) {
      return true;
    }

    return false;
  }

  String _getRequestBodyTypeName({
    required SwaggerSchema schema,
    required String modelPostfix,
    required SwaggerRoot root,
  }) {
    if (schema.type.isNotEmpty) {
      if (schema.type == kArray) {
        final ref = schema.items?.ref.getRef() ?? '';

        if (_isEnumRef(ref, root)) {
          return ref.asEnum().asList();
        }

        if (_isBasicTypeRef(ref, root)) {
          return kObject.pascalCase;
        }

        if (ref.isNotEmpty) {
          return SwaggerModelsGenerator.getValidatedClassName(ref.withPostfix(modelPostfix)).asList();
        }

        return '';
      }

      return kBasicTypesMap[schema.type] ?? schema.type;
    }

    if (schema.ref.isNotEmpty) {
      if (_isEnumRef(schema.ref, root)) {
        return schema.ref.getRef().asEnum();
      }

      if (_isBasicTypeRef(schema.ref, root)) {
        return kObject.pascalCase;
      }

      return SwaggerModelsGenerator.getValidatedClassName(schema.ref.getRef().withPostfix(modelPostfix));
    }

    return '';
  }

  Code? _getHeaderDefaultValue(SwaggerRequestParameter swaggerParameter, GeneratorOptions options) {
    final overridenValue = options.defaultHeaderValuesMap
        .firstWhereOrNull((map) => map.headerName.toLowerCase() == swaggerParameter.name.toLowerCase());

    if (overridenValue != null) {
      return Code('\'${overridenValue.defaultValue}\'');
    }

    return null;
  }

  String _getRequestMethodName({
    required SwaggerRequest swaggerRequest,
    required GeneratorOptions options,
    required String path,
    required String requestType,
  }) {
    return SwaggerModelsGenerator.generateRequestName(path, requestType);
  }

  static SwaggerResponse? getSuccessedResponse({
    required Map<String, SwaggerResponse> responses,
  }) {
    return responses.entries
        .firstWhereOrNull((responseEntry) =>
            successResponseCodes.contains(responseEntry.key) ||
            successDescriptions.contains(responseEntry.value.description))
        ?.value;
  }

  String _getResponseModelName({
    required String path,
    required String methodName,
    required String modelPostfix,
  }) {
    return '${methodName.pascalCase}\$$kResponse$modelPostfix';
  }

  String? _getReturnTypeFromType(SwaggerResponse swaggerResponse, String modelPostfix) {
    final responseType = swaggerResponse.schema?.type ?? '';
    if (responseType.isEmpty) {
      return null;
    }

    if (responseType == kArray) {
      final itemsOriginalRef = swaggerResponse.schema?.items?.originalRef;
      final itemsType = swaggerResponse.schema?.items?.type;
      final itemsRef = swaggerResponse.schema?.items?.ref.getRef();

      final arrayType =
          [itemsRef, itemsOriginalRef, itemsType, kObject].firstWhere((element) => element?.isNotEmpty == true)!;

      final mappedArrayType = kBasicTypesMap[arrayType] ?? arrayType;

      if (mappedArrayType.isEmpty) {
        return null;
      }

      return _mapParameterName(mappedArrayType, modelPostfix).asList();
    }

    return kBasicTypesMap[responseType] ?? responseType + modelPostfix;
  }

  String? _getReturnTypeFromSchema(SwaggerResponse swaggerResponse, String modelPostfix, SwaggerRoot root) {
    final listRef = swaggerResponse.schema?.items?.ref ?? '';

    if (listRef.isNotEmpty) {
      return (listRef.getRef() + modelPostfix).asList();
    }

    final ref = swaggerResponse.schema?.ref ?? swaggerResponse.ref;

    if (ref.isNotEmpty) {
      final allReusableObjects = _getAllReusableObjects(root);
      final neededResponse = allReusableObjects[ref.getUnformattedRef()];

      if (neededResponse == null) {
        return kObject.pascalCase;
      }

      if (neededResponse.ref.isNotEmpty) {
        return kObject.pascalCase;
      }

      return ref.getRef() + modelPostfix;
    }

    return null;
  }

  Map<String, SwaggerSchema> _getAllReusableObjects(SwaggerRoot root) {
    final results = <String, SwaggerSchema>{};
    results.addAll(root.definitions);
    results.addAll(root.components?.schemas ?? {});
    results.addAll(root.components?.responses ?? {});
    results.addAll(root.components?.requestBodies ?? {});

    return results;
  }

  String? _getReturnTypeFromOriginalRef(SwaggerResponse swaggerResponse, String modelPostfix) {
    if (swaggerResponse.schema?.originalRef.isNotEmpty == true) {
      return swaggerResponse.schema!.originalRef + modelPostfix;
    }

    return null;
  }

  String? _getReturnTypeFromContent({
    required SwaggerResponse swaggerResponse,
    required String modelPostfix,
    required SwaggerRoot swaggerRoot,
  }) {
    final content = swaggerResponse.content;

    if (content == null) {
      return null;
    }

    final ref = content.ref;
    if (ref.isNotEmpty) {
      final type = ref.getRef().withPostfix(modelPostfix);
      return kBasicTypesMap[type] ?? type;
    }

    final schemaRef = content.schema?.ref ?? '';
    if (schemaRef.isNotEmpty) {
      final allRefs = _getAllReusableObjects(swaggerRoot);
      final neededSchema = allRefs[schemaRef.getUnformattedRef()];

      if (neededSchema == null) {
        return kObject.pascalCase;
      }

      if (kBasicTypes.contains(neededSchema.type)) {
        return kObject.pascalCase;
      }

      final typeName = SwaggerModelsGenerator.getValidatedClassName(schemaRef.getRef()).withPostfix(modelPostfix);

      if (neededSchema.type == kArray) {
        return neededSchema.items?.ref.getRef().withPostfix(modelPostfix).asList();
      }

      return typeName;
    }

    final responseType = content.responseType;

    if (responseType.isNotEmpty) {
      if (responseType == kArray) {
        final originalRef = swaggerResponse.schema?.items?.originalRef ?? '';

        if (originalRef.isNotEmpty) {
          return kBasicTypesMap[originalRef]?.asList();
        }
      }
    }

    final itemsRef = content.items?.ref ?? '';
    if (itemsRef.isNotEmpty) {
      return kBasicTypesMap[itemsRef]?.withPostfix(modelPostfix).asList();
    }

    final schemaItemsRef = content.schema?.items?.ref ?? '';
    if (schemaItemsRef.isNotEmpty) {
      final result =
          SwaggerModelsGenerator.getValidatedClassName(schemaItemsRef.getRef()).withPostfix(modelPostfix).asList();
      return result;
    }

    final contentSchemaType = content.schema?.type ?? '';
    if (contentSchemaType.isNotEmpty == true) {
      return kBasicTypesMap[contentSchemaType];
    }

    if (responseType.isEmpty) {
      return '';
    }

    return kBasicTypesMap[responseType] ?? responseType + modelPostfix;
  }

  String _getReturnTypeName({
    required Map<String, SwaggerResponse> responses,
    required Map<String, ResponseOverrideValueMap> overridenResponses,
    required String path,
    required String methodName,
    required String modelPostfix,
    required SwaggerRoot swaggerRoot,
  }) {
    if (overridenResponses.containsKey(path)) {
      return overridenResponses[path]!.overriddenValue;
    }

    final neededResponse = getSuccessedResponse(
      responses: responses,
    );

    if (neededResponse == null) {
      return '';
    }

    if (neededResponse.schema?.type == kObject && neededResponse.schema?.properties.isNotEmpty == true) {
      return _getResponseModelName(
        path: path,
        methodName: methodName,
        modelPostfix: modelPostfix,
      );
    }

    final type = _getReturnTypeFromType(neededResponse, modelPostfix) ??
        _getReturnTypeFromSchema(neededResponse, modelPostfix, swaggerRoot) ??
        _getReturnTypeFromOriginalRef(neededResponse, modelPostfix) ??
        _getReturnTypeFromContent(
          swaggerResponse: neededResponse,
          modelPostfix: modelPostfix,
          swaggerRoot: swaggerRoot,
        ) ??
        '';

    if (type.isNotEmpty) {
      return type;
    }

    return '';
  }
}
