import { HttpMethod } from '../http';
import { SwaggerParameter } from './SwaggerParameterType';
import { SwaggerRequestBody } from './SwaggerRequestBodyType';
import { SwaggerResponse } from './SwaggerResponseType';
import { SwaggerSchema } from './SwaggerSchemaType';

class SwaggerDoc {
	private method: HttpMethod;
	private route?: string;
	private summary?: string;
	private description?: string;
	private requestBody?: SwaggerRequestBody;
	private parameters: SwaggerParameter[] = [];
	private responses: SwaggerResponse[] = [];
	private security: string[] = [];

	public static get(route: string): SwaggerDoc {
		return new SwaggerDoc(HttpMethod.GET).setRoute(route);
	}

	public static post(route: string): SwaggerDoc {
		return new SwaggerDoc(HttpMethod.POST).setRoute(route);
	}
    
	private constructor(method: HttpMethod) {
		this.method = method;
	}

	private setRoute(route: string): SwaggerDoc {
		this.route = route;
		return this;
	}

	public setSummary(summary: string): SwaggerDoc {
		this.summary = summary;
		return this;
	}

	public setDescription(description: string): SwaggerDoc {
		this.description = description;
		return this;
	}

	public setRequestBody(requestBody: SwaggerRequestBody): SwaggerDoc {
		this.requestBody = requestBody;
		return this;
	}

	public addParameter(param: SwaggerParameter): SwaggerDoc {
		this.parameters.push(param);
		return this;
	}

	public addResponse(res: SwaggerResponse): SwaggerDoc {
		this.responses.push(res);
		return this;
	}

	public addSecurityScheme(scheme: string): SwaggerDoc {
		this.security.push(scheme);
		return this;
	}

	public toAnnotation(): string {
		let annotation  =   '/**\n';
		annotation      +=  ' *\t@swagger\n';
		annotation      +=  ` *\t${this.route}:\n`;
		annotation      +=  ` *\t\t${this.getMethod()}:\n`;
        
		if(this.summary)
			annotation  +=  ` *\t\t\tsummary: ${this.summary}\n`;
        
		if(this.description)
			annotation  +=  ` *\t\t\tdescription: ${this.description}\n`;

		if(this.security.length > 0) {
			annotation	+=	' *\t\t\tsecurity:\n';
			for(const scheme of this.security) {
				annotation	+=	` *\t\t\t\t- ${scheme}: []\n`;
			}
		}
        
		if(this.parameters.length > 0) {
			annotation	+=	' *\t\t\tparameters:\n';
			for(const param of this.parameters) {
				annotation += ` *\t\t\t- in: ${param.in}\n`;
				annotation += ` *\t\t\t  name: ${param.name}\n`;
				annotation += ` *\t\t\t  required: ${param.required}\n`;
				annotation += ` *\t\t\t  description: ${param.description}\n`;
				if(param.schema) {
					annotation += ' *\t\t\t  schema:\n';
					annotation += this.deserializeSchema(param.schema, 4);
				}
			}
		}

		if(this.requestBody) {
			annotation	+=	' *\t\t\trequestBody:\n';
			annotation	+=	` *\t\t\t\trequired: ${this.requestBody.required}\n`;
			annotation	+=	` *\t\t\t\tdescription: ${this.requestBody.description}\n`;
			annotation	+=	' *\t\t\t\tcontent:\n';
			annotation	+=	` *\t\t\t\t\t${this.requestBody.content.mediaType}:\n`;
			annotation	+=	' *\t\t\t\t\t\tschema:\n';
			annotation	+=	this.deserializeSchema(this.requestBody.content.schema, 6);
		}

		if(this.responses.length > 0) {
			annotation += ' *\t\t\tresponses:\n';
			for(const res of this.responses) {
				annotation += ` *\t\t\t\t${res.code}:\n`;
				annotation += ` *\t\t\t\t\tdescription: ${res.description}\n`;
				if(res.content) {
					annotation += ' *\t\t\t\t\tcontent:\n';
					annotation += ` *\t\t\t\t\t\t${res.content.mediaType}:\n`;
					annotation += ' *\t\t\t\t\t\t\tschema:\n';
					annotation += this.deserializeSchema(res.content.schema, 7);
				}
			}
		}

		annotation		+=	' */\n';
		return annotation.replace(/\t/g, '  ');
	}

	private getMethod(): string {
		switch(this.method) {
		case HttpMethod.GET:
			return 'get';
		case HttpMethod.POST:
			return 'post';
		default:
			return 'undefined';
		}
	}

	private deserializeSchema(schema: SwaggerSchema, level: number): string {
		let res = '';
		const indent = ' *' + this.repeatStr('\t', level + 1);
		res += indent + `type: ${schema.type}\n`;

		if(schema.description)
			res += indent + `description: ${schema.description}\n`;

		if(schema.example)
			res += indent + `example: ${schema.example}\n`;

		if(schema.items && schema.items.length > 0) {
			res += indent + 'items:\n';
			for(const item of schema.items) {
				res += this.deserializeSchema(item, level + 2);
			}
		}

		if(schema.properties && Object.keys(schema.properties).length > 0) {
			res += indent + 'properties:\n';
			for(const key of Object.keys(schema.properties)) {
				res += indent + '\t' + `${key}:\n`;
				res += this.deserializeSchema(schema.properties[key], level + 3);
			}
		}

		return res;
	}

	private repeatStr(str: string, n: number): string {
		let res = '';

		for(let i = 0; i < n; i++)
			res += str;

		return res;
	}

}

export { SwaggerDoc };