/**
 * Copyright IBM Corp. 2024, 2025
 */
import { OpenAPIV2, OpenAPIV3_1, OpenAPIV3 } from "openapi-types";
import { VCSFileInfo } from "../../vcs/models/vcs-file-info.model.js";
import { OpenApiTypesEnum } from "../models/openapi-types.enum.js";
import { APICFileInfo } from "../../apic-mode/models/apic-file-info.model.js";
import { APIChandle, VCShandle } from "../../common/interfaces/file-explorer-handler.interface.js";

export type OpenApiV3Types = OpenApiTypesEnum.OPENAPI_V3 | OpenApiTypesEnum.OPENAPI_V3_1;
export type OpenApiSpecInfo = OpenAPIV2.InfoObject | OpenAPIV3.InfoObject | OpenAPIV3_1.InfoObject;
export type OpenApiSpecInfoContact = OpenAPIV2.ContactObject | OpenAPIV3.ContactObject | OpenAPIV3_1.ContactObject;
export type OpenApiSpecInfoLisence = OpenAPIV2.LicenseObject | OpenAPIV3.LicenseObject | OpenAPIV3_1.LicenseObject;
export type OpenApiSpecServer = OpenAPIV3.ServerObject | OpenAPIV3_1.ServerObject;
export type OpenApiSpecServerVariable = OpenAPIV3.ServerVariableObject | OpenAPIV3_1.ServerVariableObject
export type OpenApiPathItemObject = OpenAPIV3.PathItemObject | OpenAPIV3_1.PathItemObject | OpenAPIV2.PathItemObject;
export type OpenApiPathObject = OpenAPIV3.PathsObject | OpenAPIV3_1.PathsObject;
export type OpenApiTagObject = OpenAPIV3.TagObject | OpenAPIV3_1.TagObject | OpenAPIV2.TagObject;
export type OpenApiDocumentObject = OpenAPIV3.ExternalDocumentationObject | OpenAPIV3_1.ExternalDocumentationObject | OpenAPIV2.ExternalDocumentationObject;
export type FileAccessType = FileSystemFileHandle | VCSFileInfo | APICFileInfo;
export type DirectoryAccessType = FileSystemDirectoryHandle | VCShandle | APIChandle;

export type OpenAPISpecV3ComponentsSchema = OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject;
export type OpenAPISpecV3_1ComponentsSchema = OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObject;
export type OpenAPISpecSchema = OpenAPISpecV3ComponentsSchema | OpenAPISpecV3_1ComponentsSchema;
export type OpenAPISpecComponentsSchemaObject = OpenAPISpecV3ComponentsSchema | OpenAPIV3_1.SchemaObject;
export type OpenAPISpecComponentsSchema = Record<string, OpenAPISpecV3ComponentsSchema | OpenAPIV3_1.SchemaObject>;

export type OpenApiSpecReference = OpenAPIV3.ReferenceObject | OpenAPIV3_1.ReferenceObject;

export type OpenAPISpecV3Parameter = OpenAPIV3.ParameterObject | OpenAPIV3.ReferenceObject;
export type OpenAPISpecV3_1Parameter = OpenAPIV3_1.ParameterObject | OpenAPIV3_1.ReferenceObject;
export type OpenAPISpecParameter = OpenAPISpecV3Parameter | OpenAPISpecV3_1Parameter;

export type OpenApiSpecDocumentTypes = OpenAPIV3.Document | OpenAPIV3_1.Document;
export type OpenApiSpecDocument = OpenApiSpecDocumentTypes | OpenAPIV2.Document;
export type ApiSpecDocument = OpenAPIV2.Document | OpenApiSpecDocument;

export type OpenApiSpecV3_1Components = OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ResponseObject
    | OpenAPIV3_1.ParameterObject | OpenAPIV3_1.ExampleObject | OpenAPIV3_1.RequestBodyObject
    | OpenAPIV3_1.HeaderObject | OpenAPIV3_1.SecuritySchemeObject | OpenAPIV3_1.LinkObject
    | OpenAPIV3_1.PathItemObject | OpenAPIV3_1.ReferenceObject;

export type OpenApiSpecV3Components = OpenAPIV3.SchemaObject | OpenAPIV3.ResponseObject
    | OpenAPIV3.ParameterObject | OpenAPIV3.ExampleObject | OpenAPIV3.RequestBodyObject
    | OpenAPIV3.HeaderObject | OpenAPIV3.SecuritySchemeObject | OpenAPIV3.LinkObject | OpenAPIV3.ReferenceObject;

export type OpenApiSpecComponents = OpenApiSpecV3Components | OpenApiSpecV3_1Components;
export type OpenApiSpecComponentsKey = keyof OpenAPIV3.ComponentsObject | keyof OpenAPIV3_1.ComponentsObject
export type OpenAPISecurityRequirement = OpenAPIV3.SecurityRequirementObject | OpenAPIV3_1.SecurityRequirementObject | OpenAPIV2.SecurityRequirementObject;
export type OpenAPIV2Components = 'definitions' | 'parameters' | 'responses' | 'securityDefinitions';
export type OpenAPIV2ComponentsObject = OpenAPIV2.SchemaObject | OpenAPIV2.ParameterObject | OpenAPIV2.Response | OpenAPIV2.SecuritySchemeObject;
export type OpenAPIV2HostPath = 'host' | 'basePath' | 'consumes' | 'schemes' | 'produces';
export type OpenApiSpecResponseWithoutReference = OpenAPIV3.ResponseObject | OpenAPIV3_1.ResponseObject;
export type OpenApiSpecResponse = OpenApiSpecResponseWithoutReference | OpenApiSpecReference;
export type OpenApiSpecResponses = Record<string, OpenApiSpecResponseWithoutReference | OpenApiSpecReference>;

export type OpenApiSpecHeaderWithoutReference = OpenAPIV3.HeaderObject | OpenAPIV3_1.HeaderObject;
export type OpenApiSpecHeader = OpenApiSpecHeaderWithoutReference | OpenApiSpecReference;
export type OpenApiSpecHeaders = Record<string, OpenApiSpecHeaderWithoutReference | OpenApiSpecReference>;

export type OpenApiSpecLinkWithoutReference = OpenAPIV3.LinkObject | OpenAPIV3_1.LinkObject;
export type OpenApiSpecLink = OpenApiSpecLinkWithoutReference | OpenApiSpecReference;
export type OpenApiSpecLinks = Record<string, OpenApiSpecLinkWithoutReference | OpenApiSpecReference>;

export type OpenApiSpecMediaType = OpenAPIV3.MediaTypeObject | OpenAPIV3_1.MediaTypeObject;

export type OpenApiSpecExampleObj = OpenAPIV3.ExampleObject | OpenAPIV3_1.ExampleObject | OpenAPIV3.ReferenceObject | OpenAPIV3_1.ReferenceObject;
export type OpenAPIExamples = {
    [key: string]: OpenApiSpecExampleObj;
};


export interface IApiSpecHandler {
    /**
     * Overwrites the info object in given file with info object passed in as parameter.
     * @param openApiSpecType Identifier to denote the type of open api spec
     * @param file FileHandle or VCSFileInfo using which we'll access the file
     * @param info Updated info object that needs to be set in the file
     */
    setInfo(openApiSpecType: OpenApiTypesEnum, file: FileAccessType, info: OpenApiSpecInfo): Promise<OpenApiSpecInfo>;
    /**
     * Adds a server to the existing list of servers.
     * Note: This method is only applicable for OpenAPI v3 and v3.1
     * @param openApiSpecType Identifier to denote the type of open api spec (v3 or v3.1)
     * @param file FileHandle or VCSFileInfo using which we'll access the file
     * @param newServer New server object that needs to be added to existing list of servers 
     */
    addServer(openApiSpecType: OpenApiV3Types, file: FileAccessType, newServer: OpenApiSpecServer): Promise<OpenApiSpecServer[]>;
    /**
     * Finds and updates the matching server object from the existing list of servers, with the new server object.
     * Note: This method is only applicable for OpenAPI v3 and v3.1
     * @param openApiSpecType Identifier to denote the type of open api spec (v3 or v3.1)
     * @param file FileHandle or VCSFileInfo using which we'll access the file
     * @param prevServer Previous server object
     * @param newServer New server object that will be replaced in place of prevServer
     */
    updateServer(openApiSpecType: OpenApiV3Types, file: FileAccessType, prevServer: OpenApiSpecServer, newServer: OpenApiSpecServer): Promise<OpenApiSpecServer[]>;
    /**
     * Finds and deletes the matching server object from the existing list of servers.
     * @param openApiSpecType Identifier to denote the type of open api spec (v3 or v3.1)
     * @param file FileHandle or VCSFileInfo using which we'll access the file
     * @param server Server object that has to be deleted
     */
    deleteServer(openApiSpecType: OpenApiV3Types, file: FileAccessType, server: OpenApiSpecServer): Promise<OpenApiSpecServer[]>;

    addComponentsSchema(openApiSpecType: OpenApiTypesEnum, file: FileAccessType, key: string, newSchema: OpenAPISpecComponentsSchemaObject): Promise<OpenAPISpecComponentsSchema>;
    deleteComponentsSchema(openApiSpecType: OpenApiTypesEnum, file: FileAccessType, key: string): Promise<OpenAPISpecComponentsSchema>;
    updateComponentsSchema(openApiSpecType: OpenApiTypesEnum, file: FileAccessType, prevKey: string, newkey: string, updatedSchema: OpenAPISpecComponentsSchemaObject): Promise<OpenAPISpecComponentsSchema>;


    addComponentExample(openApiSpecType: OpenApiV3Types, file: FileAccessType, key: string, exampleObject: OpenApiSpecExampleObj): Promise<OpenAPIExamples>;
    updateComponentExample(openApiSpecType: OpenApiV3Types, file: FileAccessType, prevKey: string, currentKey: string, exampleObject: OpenApiSpecExampleObj): Promise<OpenAPIExamples>;
    deleteComponentExample(openApiSpecType: OpenApiV3Types, file: FileAccessType, key: string): Promise<OpenAPIExamples>;

    addPath(openApiSpecType: OpenApiTypesEnum, file: FileAccessType, key: string, newPath: OpenApiPathItemObject): Promise<OpenApiSpecDocument>;
    updatePath(openApiSpecType: OpenApiTypesEnum, file: FileAccessType, prevKey: string, newKey: string, newPath: OpenApiPathItemObject): Promise<OpenApiSpecDocument>;
    deletePath(openApiSpecType: OpenApiTypesEnum, file: FileAccessType, path: string): Promise<OpenApiSpecDocument>;

    addTag(openApiSpecType: OpenApiTypesEnum, file: FileAccessType, tag: OpenApiTagObject): Promise<OpenApiTagObject[]>;
    updateTag(openApiSpecType: OpenApiTypesEnum, file: FileAccessType, prevKey: string, tag: OpenApiTagObject): Promise<OpenApiTagObject[]>;
    deleteTag(openApiSpecType: OpenApiTypesEnum, file: FileAccessType, tag: string): Promise<OpenApiTagObject[]>;

    addOrUpdateDocument(openApiSpecType: OpenApiTypesEnum, file: FileAccessType, document: OpenApiDocumentObject): Promise<OpenApiDocumentObject>;
    deleteDocument(openApiSpecType: OpenApiTypesEnum, file: FileAccessType): Promise<undefined>;


    getOpenApiType(document: any): OpenApiTypesEnum;

    /**
    * Overwrites the info object in given file with info object passed in as parameter.
    * @param openApiSpecType Identifier to denote the type of open api spec
    * @param file FileHandle or VCSFileInfo using which we'll access the file
    * @param object  New  object that needs to be added to existing list
    * @param typeOfComponent Key of Component (eg) examples, parameters
    */
    addComponent(openApiSpecType: OpenApiV3Types, file: FileAccessType, key: string, object: OpenApiSpecComponents, typeOfComponent: keyof OpenAPIV3.ComponentsObject | keyof OpenAPIV3_1.ComponentsObject): Promise<OpenApiSpecComponents>;
    /**
     * Finds and updates the matching  object from the existing list, with the new  object.
     * @param openApiSpecType Identifier to denote the type of open api spec (v3 or v3.1)
     * @param file FileHandle or VCSFileInfo using which we'll access the file
     * @param prevKey Previous key of the object
     * @param currentKey New key that will be replaced in place of prevKey
     * @param object New object that will be replaced in the place of prevKey
     * @param typeOfComponent Key of Component (eg) examples, parameters
     */
    updateComponent(openApiSpecType: OpenApiV3Types, file: FileAccessType, prevKey: string, currentKey: string, object: OpenApiSpecComponents, typeOfComponent: keyof OpenAPIV3.ComponentsObject | keyof OpenAPIV3_1.ComponentsObject): Promise<OpenApiSpecComponents>;
    /**
    * Finds and deletes the matching  object from the existing list.
    * @param openApiSpecType Identifier to denote the type of open api spec (v3 or v3.1)
    * @param file FileHandle or VCSFileInfo using which we'll access the file
    * @param key Key of the object that has to be deleted
    * @param typeOfComponent Key of Component (eg) examples, parameters
    */
    deleteComponent(openApiSpecType: OpenApiV3Types, file: FileAccessType, key: string, typeOfComponent: keyof OpenAPIV3.ComponentsObject | keyof OpenAPIV3_1.ComponentsObject): Promise<OpenApiSpecComponents>;
    /**
     * Updates the given record of data
     * @param openApiSpecType Identifier to denote the type of open api spec (v3 or v3.1)
     * @param data New record to update in the component
     * @param typeOfComponent Key of Component (eg) examples, parameters
     * @returns Returns the whole specification document object
     */
    setComponent: (openApiSpecType: OpenApiV3Types, file: FileAccessType, data: Record<string, OpenApiSpecComponents>, typeOfComponent: keyof OpenAPIV3.ComponentsObject | keyof OpenAPIV3_1.ComponentsObject) => Promise<OpenApiSpecDocument>;


    addSecurityRequirement(openApiSpecType: OpenApiTypesEnum, file: FileAccessType, securityRequirementObj: OpenAPISecurityRequirement[]): Promise<OpenAPISecurityRequirement[]>;
    updateSecurityRequirement(openApiSpecType: OpenApiTypesEnum, file: FileAccessType, prevsecurityRequirementName: string, securityRequirementObj: OpenAPISecurityRequirement): Promise<OpenAPISecurityRequirement[]>;
    deleteSecurityRequirement(openApiSpecType: OpenApiTypesEnum, file: FileAccessType, securityRequirementName: string): Promise<OpenAPISecurityRequirement[]>;
    /**
     * Returns a list of the HTTP Methods for the given pathItem object for OpenAPI spec versions 3.0 & 3.1
     * @param pathItem The path item object
     * @returns A list of HTTPMethods from the given path item object
     */
    getPresentHttpMethods(pathItem: OpenApiPathItemObject): OpenAPIV3.HttpMethods[];
    /**
      * Overwrites the info object in given file with info object passed in as parameter.
      * @param file FileHandle or VCSFileInfo using which we'll access the file
      * @param object  New  object that needs to be added to existing list
      * @param typeOfComponent Key of Component (eg) examples, parameters
      */
    addComponentV2(file: FileAccessType, key: string, object: OpenAPIV2ComponentsObject, typeOfComponent: OpenAPIV2Components): Promise<OpenApiSpecDocument>;
    /**
     * Finds and updates the matching  object from the existing list, with the new  object.
     * @param file FileHandle or VCSFileInfo using which we'll access the file
     * @param prevKey Previous key of the object
     * @param currentKey New key that will be replaced in place of prevKey
     * @param object New object that will be replaced in the place of prevKey
     * @param typeOfComponent Key of Component (eg) examples, parameters
     */
    updateComponentV2(file: FileAccessType, prevKey: string,
        currentKey: string, object: OpenAPIV2ComponentsObject,
        typeOfComponent: OpenAPIV2Components): Promise<OpenApiSpecDocument>;
    /**
    * Finds and deletes the matching  object from the existing list.
    * @param file FileHandle or VCSFileInfo using which we'll access the file
    * @param key Key of the object that has to be deleted
    * @param typeOfComponent Key of Component (eg) examples, parameters
    */
    deleteComponentV2(file: FileAccessType, key: string, typeOfComponent: OpenAPIV2Components): Promise<OpenApiSpecDocument>;

    /**
     * Sets component for the given type of component
     * @param file FileHandle or VCSFileInfo using which we'll access the file
     * @param data Record of the definition to be saved
     * @param typeOfComponent Key of Component (eg) examples, parameters
     */
    setComponentV2(file: FileAccessType, data: Record<string, OpenAPIV2ComponentsObject>, typeOfComponent: OpenAPIV2Components): Promise<OpenApiSpecDocument>;

    setHostPathV2(file: FileAccessType, name: string | string[], type: OpenAPIV2HostPath): Promise<OpenApiSpecDocument>;



};