import { Diagnosis, FosterCareHistory, Paging } from "entity";
import { NextFunction, Response } from "express";
import { Request } from "express-jwt";
import Joi from "joi";
import { ILogObj, Logger } from "tslog";
import { Brackets } from "typeorm";
import { validate as validateUuid } from "uuid";
import CommonHelper from "../helpers/common";
import { ReturnHelper } from "../helpers/express/return";
import { Language } from "../langs/lang";
import { FosterCareHistoryModel } from "../model/fostercarehistory";
import { OrmHelper } from "../helpers/orm";
import moment from "moment";

const log: Logger<ILogObj> = new Logger({
	name: "[FosterCareHistoryController]",
	type: "pretty",
});

export class FosterCareHistoryController {
	static async list(req: Request, res: Response, next: NextFunction): Promise<void | Response> {
		/*
			#swagger.tags = ['Foster Care History']
			#swagger.security = [{
				"bearerAuth": []
			}]
			#swagger.parameters['filter'] = {
				description: 'Filter: JSON object; FosterCareHistory.code and FosterCareHistory.diagnosis are combined with OR; other fields use AND.',
				in: 'query',
				type: 'string',
			}
			#swagger.parameters['limit'] = {
				in: 'query',
				required: true,
				type: 'number'
			}
			#swagger.parameters['page'] = {
				in: 'query',
				required: true,
				type: 'number'
			}
			#swagger.parameters['with_deleted'] = {
				in: 'query',
				required: true,
				type: 'boolean'
			}
			#swagger.parameters['order_field'] = {
				in: 'query',
				required: true,
				type: 'string'
			}
			#swagger.parameters['order_direction'] = {
				in: 'query',
				required: true,
				schema: {
					'@enum': ['ASC', 'DESC']
				}
			}
		*/

		try {
			const schema = Joi.object().keys({
				filter: Joi.string().allow("").optional().label("Filter"),
				page: Joi.number().required().min(1).label("Page"),
				limit: Joi.number().required().min(1).label("Limit"),
				with_deleted: Joi.bool().required().label("With Deleted"),
				order_field: Joi.string().required().label("Order Field"),
				order_direction: Joi.string().allow("asc", "desc").required().label("Order Direction"),
			});

			let param: Paging = await schema.validateAsync(req.query);

			let filter = param.filter && param.filter !== "" ? JSON.parse(param.filter) : {};

			const codeKey = "FosterCareHistory.code";
			const diagnosisKey = "FosterCareHistory.diagnosis";
			const orCode = filter[codeKey];
			const orDiagnosis = filter[diagnosisKey];
			const restFilter = { ...filter };
			delete restFilter[codeKey];
			delete restFilter[diagnosisKey];

			var query = await FosterCareHistoryModel.list(restFilter);

			if (orCode !== undefined || orDiagnosis !== undefined) {
				query.andWhere(
					new Brackets((qb) => {
						let isFirst = true;
						const addOr = (column: string, paramKey: string, raw: any) => {
							const op = isFirst ? "where" : "orWhere";
							isFirst = false;
							if (Array.isArray(raw)) {
								(qb as any)[op](`${column} IN (:...${paramKey})`, { [paramKey]: raw });
								return;
							}
							const f = String(raw);
							if (validateUuid(f)) {
								(qb as any)[op](`${column} = :${paramKey}`, { [paramKey]: f });
								return;
							}
							if (f === "null") {
								(qb as any)[op](`${column} IS NULL`);
								return;
							}
							if (f.includes("%")) {
								(qb as any)[op](`${column} ILIKE :${paramKey}`, { [paramKey]: f });
								return;
							}
							(qb as any)[op](`${column} = :${paramKey}`, { [paramKey]: f });
						};
						if (orCode !== undefined) addOr("FosterCareHistory.code", "diag_or_code", orCode);
						if (orDiagnosis !== undefined) addOr("FosterCareHistory.diagnosis", "diag_or_diagnosis", orDiagnosis);
					}),
				);
			}

			const offset = (param.page - 1) * param.limit;
			let limit = param.limit;

			const res_count = query;
			const res_list = query
				.orderBy("FosterCareHistory." + param.order_field, param.order_direction)
				.offset(offset)
				.limit(limit);

			if (param.with_deleted) {
				res_count.withDeleted();
				res_list.withDeleted();
			}

			const current_page = param.page;
			const total_count_data = await res_count.getCount();
			const list_data = await res_list.getMany();
			const count_data = CommonHelper.countObject(list_data);

			return ReturnHelper.successResponselist(res, 200, Language.lang.success_view, count_data, current_page, total_count_data, list_data);
		} catch (e: unknown) {
			log.error(e);
			const err = e as Error;

			return ReturnHelper.errorResponse(res, 400, 401, Language.lang.failed_view, err.message);
		}
	}

	static async create(req: Request, res: Response, next: NextFunction): Promise<void | Response> {
		/*
		 #swagger.tags = ['Foster Care History']
		 #swagger.security = [{
			 "bearerAuth": []
		 }]
		 #swagger.requestBody = {
			 required: true,
			 content: {
				 "application/json": {
					 schema: {
						 $ref: "#/components/schemas/fostercarehistory"
					 }
				 }
			 }
		 }
		 */
		const queryRunner = OrmHelper.DB.createQueryRunner();
		await queryRunner.startTransaction();
		try {
			const schema = Joi.object().keys({
				diagnosis: Joi.string().max(256).required().label("Diagnosis"),
				code: Joi.string().max(128).required().label("Code"),
				status: Joi.string().required().label("Status"),
				category: Joi.string().max(128).allow(null, "").label("Category"),
				subcategory: Joi.string().max(128).allow(null, "").label("Subcategory"),
				definition: Joi.string().max(128).allow(null, "").label("Definition"),
				objective: Joi.string().max(128).allow(null, "").label("Objective"),
				result_criteria: Joi.array().items(
					Joi.object({
						group: Joi.number().allow(null),
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Result Criteria"),
				causes: Joi.array().items(
					Joi.object({
						group: Joi.string().allow(null, ""),
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Causes"),
				related_clinical_condition: Joi.array().items(
					Joi.object({
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Related Clinical Condition"),
				subjective_major: Joi.array().items(
					Joi.object({
						group: Joi.string().allow(null, ""),
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Subjective Major"),
				objectitve_major: Joi.array().items(
					Joi.object({
						group: Joi.string().allow(null, ""),
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Objective Major"),
				subjective_minor: Joi.array().items(
					Joi.object({
						group: Joi.string().allow(null, ""),
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Subjective Minor"),
				objective_minor: Joi.array().items(
					Joi.object({
						group: Joi.string().allow(null, ""),
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Objective Minor"),
				observation: Joi.array().items(
					Joi.object({
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Observation"),
				therapeutic: Joi.array().items(
					Joi.object({
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Therapeutic"),
				education: Joi.array().items(
					Joi.object({
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Education"),
				colaboration: Joi.array().items(
					Joi.object({
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Colaboration"),
			});

			let param: any = await schema.validateAsync(req.body);

			let filter = {
				code: param.code,
			};
			let exist = await FosterCareHistoryModel.list(filter).then((q) => q.getOne());
			if (exist) throw { message: "Diagnosis " + Language.lang.failed_duplicate };

			let fostercarehistory = new FosterCareHistory();
			fostercarehistory.diagnosis = param.diagnosis;
			fostercarehistory.code = param.code;
			fostercarehistory.status = param.status;
			fostercarehistory.category = param.category;
			fostercarehistory.subcategory = param.subcategory;
			fostercarehistory.definition = param.definition;
			fostercarehistory.objective = param.objective;
			fostercarehistory.result_criteria = param.result_criteria ?? [];
			fostercarehistory.causes = param.causes ?? [];
			fostercarehistory.related_clinical_condition = param.related_clinical_condition ?? [];
			fostercarehistory.subjective_major = param.subjective_major ?? [];
			fostercarehistory.objectitve_major = param.objectitve_major ?? [];
			fostercarehistory.subjective_minor = param.subjective_minor ?? [];
			fostercarehistory.objective_minor = param.objective_minor ?? [];
			fostercarehistory.observation = param.observation ?? [];
			fostercarehistory.therapeutic = param.therapeutic ?? [];
			fostercarehistory.education = param.education ?? [];
			fostercarehistory.colaboration = param.colaboration ?? [];
			fostercarehistory.created_by = req.auth?.data.name;
			fostercarehistory.updated_by = req.auth?.data.name;
			await queryRunner.manager.save(fostercarehistory);

			await queryRunner.commitTransaction();

			return ReturnHelper.successResponseAny(res, 200, Language.lang.success_insert, fostercarehistory);
		} catch (e: unknown) {
			await queryRunner.rollbackTransaction();
			log.error(e);
			const err = e as Error;
			return ReturnHelper.errorResponse(res, 400, 401, Language.lang.failed_view, err.message);
		} finally {
			await queryRunner.release();
		}
	}

	static async detail(req: Request, res: Response, next: NextFunction): Promise<void | Response> {
		/*
		 #swagger.tags = ['Foster Care History']
		 #swagger.security = [{
			 "bearerAuth": []
		 }]
		#swagger.parameters['id'] = {
			description:'',
			in: 'path',
			type: 'string'
		}
		*/

		try {
			const schema = Joi.object().keys({
				id: Joi.string().uuid().required().label("Foster Care History Id"),
			});

			let param: any = await schema.validateAsync(req.params);

			let fostercarehistory = await FosterCareHistoryModel.list({
				"FosterCareHistory.id": param.id,
			}).then((q) => q.getOne());

			if (!fostercarehistory) throw { message: "Foster Care History Not Found" };

			return ReturnHelper.successResponseAny(res, 200, Language.lang.success_view, fostercarehistory);
		} catch (e: unknown) {
			log.error(e);
			const err = e as Error;

			return ReturnHelper.errorResponse(res, 400, 401, Language.lang.failed_not_found, err.message);
		}
	}

	static async update(req: Request, res: Response, next: NextFunction): Promise<void | Response> {
		/*
		 #swagger.tags = ['Foster Care History']
		 #swagger.security = [{
			 "bearerAuth": []
		 }]
		#swagger.parameters['id'] = {
			description:'',
			in: 'path',
			type: 'string'
		}
		 #swagger.requestBody = {
			 required: true,
			 content: {
				 "application/json": {
					 schema: {
						 $ref: "#/components/schemas/fostercarehistory"
					 }
				 }
			 }
		 }
		 */

		const queryRunner = OrmHelper.DB.createQueryRunner();
		await queryRunner.startTransaction();

		try {
			req.body.id = req.params["id"];

			const schema = Joi.object().keys({
				id: Joi.string().uuid().required().label("Diagnosis ID"),
				diagnosis: Joi.string().max(256).required().label("Diagnosis"),
				code: Joi.string().max(128).required().label("Code"),
				status: Joi.string().required().label("Status"),
				category: Joi.string().max(128).allow(null, "").label("Category"),
				subcategory: Joi.string().max(128).allow(null, "").label("Subcategory"),
				definition: Joi.string().max(128).allow(null, "").label("Definition"),
				objective: Joi.string().max(128).allow(null, "").label("Objective"),
				result_criteria: Joi.array().items(
					Joi.object({
						group: Joi.number().allow(null),
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Result Criteria"),
				causes: Joi.array().items(
					Joi.object({
						group: Joi.string().allow(null, ""),
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Causes"),
				related_clinical_condition: Joi.array().items(
					Joi.object({
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Related Clinical Condition"),
				subjective_major: Joi.array().items(
					Joi.object({
						group: Joi.string().allow(null, ""),
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Subjective Major"),
				objectitve_major: Joi.array().items(
					Joi.object({
						group: Joi.string().allow(null, ""),
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Objective Major"),
				subjective_minor: Joi.array().items(
					Joi.object({
						group: Joi.string().allow(null, ""),
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Subjective Minor"),
				objective_minor: Joi.array().items(
					Joi.object({
						group: Joi.string().allow(null, ""),
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Objective Minor"),
				observation: Joi.array().items(
					Joi.object({
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Observation"),
				therapeutic: Joi.array().items(
					Joi.object({
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Therapeutic"),
				education: Joi.array().items(
					Joi.object({
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Education"),
				colaboration: Joi.array().items(
					Joi.object({
						name: Joi.string().allow(null, ""),
					})
				).allow(null).label("Colaboration"),
			});

			let param: any = await schema.validateAsync(req.body);

			let filter = {
				code: param.code,
			};
			let exist = await FosterCareHistoryModel.list(filter).then((q) => q.getOne());
			if (exist && exist.id != param.id) throw { message: "Foster Care History " + Language.lang.failed_duplicate };

			let fostercarehistory = await FosterCareHistoryModel.list({
				"FosterCareHistory.id": param.id,
			}).then((q) => q.getOne());

			if (!fostercarehistory) throw { message: "FosterCareHistory " + Language.lang.failed_not_found };

			fostercarehistory.diagnosis = param.diagnosis;
			fostercarehistory.code = param.code;
			fostercarehistory.status = param.status;
			fostercarehistory.category = param.category;
			fostercarehistory.subcategory = param.subcategory;
			fostercarehistory.definition = param.definition;
			fostercarehistory.objective = param.objective;
			fostercarehistory.result_criteria = param.result_criteria ?? fostercarehistory.result_criteria;
			fostercarehistory.causes = param.causes ?? fostercarehistory.causes;
			fostercarehistory.related_clinical_condition = param.related_clinical_condition ?? fostercarehistory.related_clinical_condition;
			fostercarehistory.subjective_major = param.subjective_major ?? fostercarehistory.subjective_major;
			fostercarehistory.objectitve_major = param.objectitve_major ?? fostercarehistory.objectitve_major;
			fostercarehistory.subjective_minor = param.subjective_minor ?? fostercarehistory.subjective_minor;
			fostercarehistory.objective_minor = param.objective_minor ?? fostercarehistory.objective_minor;
			fostercarehistory.observation = param.observation ?? fostercarehistory.observation;
			fostercarehistory.therapeutic = param.therapeutic ?? fostercarehistory.therapeutic;
			fostercarehistory.education = param.education ?? fostercarehistory.education;
			fostercarehistory.colaboration = param.colaboration ?? fostercarehistory.colaboration;
			fostercarehistory.updated_by = req.auth?.data.name;
			await queryRunner.manager.save(fostercarehistory);

			await queryRunner.commitTransaction();

			return ReturnHelper.successResponseAny(res, 200, Language.lang.success_update, fostercarehistory);
		} catch (e: unknown) {
			await queryRunner.rollbackTransaction();
			log.error(e);
			const err = e as Error;
			return ReturnHelper.errorResponse(res, 400, 401, Language.lang.failed_view, err.message);
		} finally {
			await queryRunner.release();
		}
	}

	static async delete(req: Request, res: Response, next: NextFunction): Promise<Response> {
		/*
		#swagger.tags = ['Foster Care History']
		#swagger.security = [{
			"bearerAuth": []
		}]
		#swagger.parameters['id'] = {
			in: 'path',
			description: 'Foster Care History ID.',
			required: true,
			type: 'string'
		}
		#swagger.parameters['hard'] = {
			in: 'path',
			description: 'Is Hard Delete',
			required: false,
			type: 'boolean'
		}
		*/
		try {
			const schema = Joi.object().keys({
				id: Joi.string().uuid().required().label("ID"),
				hard: Joi.bool().optional().allow("").label("Is hard delete?"),
			});

			const param: { id: string; hard: boolean } = await schema.validateAsync(req.params);

			let affected = 0;
			if (!param.hard) {
				let fostercarehistory: FosterCareHistory = await FosterCareHistoryModel.list({
					"FosterCareHistory.id": param.id,
				}).then((q) => q.getOne());

				if (!fostercarehistory) throw { message: "Foster Care History " + Language.lang.failed_not_found };

				fostercarehistory.deleted_by = req.auth?.data.name;
				fostercarehistory.deleted_at = moment().toDate();
				await OrmHelper.DB.manager.save(fostercarehistory);
				affected = 1;
			} else {
				const repo = OrmHelper.DB.getRepository(FosterCareHistory);
				affected = (await repo.delete({ id: param.id })).affected ?? 0;
			}

			if (affected > 0) {
				return ReturnHelper.successResponseAny(res, 200, Language.lang.success_delete, {});
			} else {
				return ReturnHelper.errorResponse(res, 404, 401, Language.lang.failed_not_found, "");
			}
		} catch (e: unknown) {
			log.error(e);
			const err = e as Error;

			return ReturnHelper.errorResponse(res, 500, 402, Language.lang.failed_delete, err.message);
		}
	}

	static async restore(req: Request, res: Response, next: NextFunction): Promise<Response> {
		/*
		#swagger.tags = ['Foster Care History']
		#swagger.security = [{
			"bearerAuth": []
		}]
		#swagger.parameters['id'] = {
			in: 'path',
			description: 'Foster Care History ID.',
			required: true,
			type: 'string'
		}
		*/
		try {
			const schema = Joi.object().keys({
				id: Joi.string().uuid().required().label("ID"),
			});

			const param: any = await schema.validateAsync(req.params);

			const repo = OrmHelper.DB.getRepository(FosterCareHistory);

			const affected = (await repo.restore({ id: param.id })).affected ?? 0;

			if (affected > 0) {
				return ReturnHelper.successResponseAny(res, 200, Language.lang.success_restore, {});
			} else {
				return ReturnHelper.errorResponse(res, 404, 401, Language.lang.failed_not_found, "");
			}
		} catch (e: unknown) {
			log.error(e);
			const err = e as Error;

			return ReturnHelper.errorResponse(res, 500, 402, Language.lang.failed_restore, err.message);
		}
	}
}
