import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import { ILogerService, ConsoleLogType } from './common.interface';

/**
 * Api service
 *
 * Axios 를 이용하여 서버와 통신을 하는 서비스입니다
 *
 * ```ts
 * apiService.get("api/codes/AS001").then(result => {
 * 	console.log(result);
 * })
 * ```
 */
export class ApiService implements ILogerService {
	/**
	 * Instance  of api service
	 */
	private static _instance: ApiService | null = null;

	/**
	 * Axios  of api service
	 */
	private _axios: AxiosInstance = null;

	/**
	 * Creates an instance of api service.
	 */
	constructor() {
		this._axios = axios.create({
			baseURL: process.env.VUE_APP_API_URL,
			timeout: 20000,
			headers: {
				'Content-Type': 'application/json',
				Accept: '*/*',
			},
			withCredentials: true,
		});
	}

	/**
	 * Gets instance
	 *
	 * @returns instance
	 */
	public static getInstance(): ApiService {
		if (this._instance === null) {
			this._instance = new ApiService();
		}
		return this._instance;
	}

	/**
	 * Gets promise
	 *
	 * @param promise
	 * @returns promise
	 */
	private getPromise(promise: Promise<AxiosResponse<any, any>>): Promise<any> {
		return new Promise<any>((resolve, reject) => {
			promise
				.then((response) => {
					if (process.env.NODE_ENV !== 'production') {
						switch (response?.request?.status) {
							case 200:
								this.consoleLog('debug', 'Response 200', response?.request?.responseURL, response?.data);
								break;
							default:
								this.consoleLog('debug', 'Response ' + response?.request?.status, response?.request?.responseURL, response?.data);
								break;
						}
					}
					if (response.data) {
						if (response.data.message || response.data.busiMessage) {
							reject(response.data);
						} else {
							resolve(response.data);
						}
					} else {
						resolve(null);
					}
				})
				.catch((error) => {
					if (error.response) {
						// The request was made and the server responded with a status code
						// that falls out of the range of 2xx
						this.consoleLog('debug', 'Error Response', error.message, error.response.data, error.response.status, error.response.headers);
					} else if (error.request) {
						// The request was made but no response was received
						// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
						// http.ClientRequest in node.js
						this.consoleLog('debug', 'Error Request', '' + error.request);
					} else {
						// Something happened in setting up the request that triggered an Error
						this.consoleLog('debug', 'Error Message', '' + error.message);
					}
					this.consoleLog('debug', 'Error Config', error);
					reject(error?.response?.data || error);
				});
		});
	}

	/**
	 * Logger  of api service
	 */
	private _logger: ILogerService = null;

	/**
	 * Sets logger
	 *
	 * @param logger
	 */
	public setLogger(logger: ILogerService) {
		this._logger = logger;
	}

	/**
	 * Consoles log
	 *
	 * @param title
	 * @param obj
	 */
	public consoleLog(type: ConsoleLogType, title: string, ...obj: any[]) {
		if (this._logger !== null) {
			this._logger.consoleLog(type, title, ...obj);
		}
	}

	/**
	 * Gets api service
	 *
	 * @param url
	 * @param [params]
	 * @param [config]
	 * @returns get
	 */
	public get(url: string, params?: any, config?: AxiosRequestConfig): Promise<any> {
		if (params) {
			if (!config) {
				config = {};
			}
			config.params = params;
		}
		return this.getPromise(this._axios.get(url, config));
	}

	/**
	 * Deletes api service
	 *
	 * @param url
	 * @param [params]
	 * @param [config]
	 * @returns delete
	 */
	public delete(url: string, params?: any, config?: AxiosRequestConfig): Promise<any> {
		if (params) {
			if (!config) {
				config = {};
			}
			config.params = params;
		}
		return this.getPromise(this._axios.delete(url, config));
	}

	/**
	 * Posts api service
	 *
	 * @param url
	 * @param [data]
	 * @param [config]
	 * @returns post
	 */
	public post(url: string, data?: any, config?: AxiosRequestConfig): Promise<any> {
		return this.getPromise(this._axios.post(url, data, config));
	}

	/**
	 * Puts api service
	 *
	 * @param url
	 * @param [data]
	 * @param [config]
	 * @returns put
	 */
	public put(url: string, data?: any, config?: AxiosRequestConfig): Promise<any> {
		return this.getPromise(this._axios.put(url, data, config));
	}

	/**
	 * Patchs api service
	 *
	 * @param url
	 * @param [data]
	 * @param [config]
	 * @returns patch
	 */
	public patch(url: string, data?: any, config?: AxiosRequestConfig): Promise<any> {
		return this.getPromise(this._axios.patch(url, data, config));
	}
}

export default ApiService.getInstance();
