import { FormBuilder, FormControl, FormGroup, ValidatorFn, Validators } from "@angular/forms";
import { Guid } from '@common/types/guid';
import { ValidationType, ValidatorURL } from "../../../core/common/enum/validation-type";
import { BaseFormModel } from "../../../core/model/base-form-model";
import { CompositeField } from "../../../core/model/dataset-profile-definition/composite-field";
import { DatasetProfileDefinitionModel } from "../../../core/model/dataset-profile-definition/dataset-profile-definition";
import { DefaultValue } from "../../../core/model/dataset-profile-definition/default-value";
import { Field } from "../../../core/model/dataset-profile-definition/field";
import { Multiplicity } from "../../../core/model/dataset-profile-definition/multiplicity";
import { Page } from "../../../core/model/dataset-profile-definition/page";
import { Rule } from "../../../core/model/dataset-profile-definition/rule";
import { Section } from "../../../core/model/dataset-profile-definition/section";
import { ViewStyle } from "../../../core/model/dataset-profile-definition/view-style";
import { BackendErrorValidator } from '@common/forms/validation/custom-validator';
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';

export class DatasetDescriptionFormEditorModel extends BaseFormModel {

	public status: number;
	public pages: Array<DatasetDescriptionPageEditorModel> = [];
	public rules: Rule[] = [];
	public validationErrorModel: ValidationErrorModel = new ValidationErrorModel();

	fromModel(item: DatasetProfileDefinitionModel): DatasetDescriptionFormEditorModel {
		this.status = item.status;
		this.rules = item.rules;
		if (item.pages) { item.pages.map(x => this.pages.push(new DatasetDescriptionPageEditorModel().fromModel(x))); }
		return this;
	}

	buildForm(): FormGroup {
		const formGroup: FormGroup = new FormBuilder().group({
			rules: [{ value: this.rules, disabled: false }, BackendErrorValidator(this.validationErrorModel, 'rules')]
		});
		const pagesFormArray = new Array<FormGroup>();

		this.pages.forEach(item => {
			const form: FormGroup = item.buildForm();
			pagesFormArray.push(form);
		});
		formGroup.addControl('pages', this.formBuilder.array(pagesFormArray));
		return formGroup;
	}
}

export class DatasetDescriptionPageEditorModel extends BaseFormModel {
	public ordinal: number;
	public title: string;
	public sections: DatasetDescriptionSectionEditorModel[] = [];

	fromModel(item: Page): DatasetDescriptionPageEditorModel {
		this.ordinal = item.ordinal;
		this.title = item.title;
		if (item.sections) { item.sections.map(x => this.sections.push(new DatasetDescriptionSectionEditorModel().fromModel(x))); }
		return this;
	}

	buildForm(): FormGroup {
		const formGroup: FormGroup = new FormBuilder().group({});
		const sectionsFormArray = new Array<FormGroup>();
		this.sections.forEach(item => {
			const form: FormGroup = item.buildForm();
			sectionsFormArray.push(form);
		});
		formGroup.addControl('sections', this.formBuilder.array(sectionsFormArray));
		formGroup.addControl('title', new FormControl({ value: this.title, disabled: true }));
		formGroup.addControl('ordinal', new FormControl({ value: this.ordinal, disabled: true }));
		return formGroup;
	}
}

export class DatasetDescriptionSectionEditorModel extends BaseFormModel {

	sections: Array<DatasetDescriptionSectionEditorModel> = new Array<DatasetDescriptionSectionEditorModel>();
	defaultVisibility: boolean;
	page: number;
	numbering: string;
	ordinal: number;
	id: string;
	title: string;
	description: string;
	compositeFields: Array<DatasetDescriptionCompositeFieldEditorModel> = new Array<DatasetDescriptionCompositeFieldEditorModel>();

	fromModel(item: Section): DatasetDescriptionSectionEditorModel {
		if (item.sections) { item.sections.map(x => this.sections.push(new DatasetDescriptionSectionEditorModel().fromModel(x))); }
		this.page = item.page;
		this.defaultVisibility = item.defaultVisibility;
		this.numbering = item.numbering;
		this.id = item.id;
		this.title = item.title;
		this.ordinal = item.ordinal;
		this.description = item.description;
		if (item.compositeFields) { item.compositeFields.map(x => this.compositeFields.push(new DatasetDescriptionCompositeFieldEditorModel().fromModel(x))); }
		return this;
	}

	buildForm(): FormGroup {
		const formGroup: FormGroup = new FormBuilder().group({});
		const sectionsFormArray = new Array<FormGroup>();
		if (this.sections) {
			this.sections.forEach(item => {
				const form: FormGroup = item.buildForm();
				sectionsFormArray.push(form);
			});
		}
		const compositeFieldsFormArray = new Array<FormGroup>();
		if (this.compositeFields) {
			this.compositeFields.forEach(item => {
				const form: FormGroup = item.buildForm();
				compositeFieldsFormArray.push(form);
			});
		}
		formGroup.addControl('compositeFields', this.formBuilder.array(compositeFieldsFormArray));
		formGroup.addControl('sections', this.formBuilder.array(sectionsFormArray));
		formGroup.addControl('description', new FormControl({ value: this.description, disabled: true }));
		formGroup.addControl('numbering', new FormControl({ value: this.numbering, disabled: true }));
		formGroup.addControl('title', new FormControl({ value: this.title, disabled: true }));
		formGroup.addControl('id', new FormControl({ value: this.id, disabled: false }));
		formGroup.addControl('ordinal', new FormControl({ value: this.ordinal, disabled: true }));
		return formGroup;
	}
}

export class DatasetDescriptionCompositeFieldEditorModel extends BaseFormModel {

	fields: Array<DatasetDescriptionFieldEditorModel> = new Array<DatasetDescriptionFieldEditorModel>();
	ordinal: number;
	id: string;
	numbering: string;
	multiplicity: DatasetDescriptionMultiplicityEditorModel;
	multiplicityItems: DatasetDescriptionCompositeFieldEditorModel[] = [];
	title: string;
	description: string;
	extendedDescription: string;
	additionalInformation: string;
	hasCommentField: boolean;
	commentFieldValue: string;

	fromModel(item: CompositeField): DatasetDescriptionCompositeFieldEditorModel {

		if (item.fields) { item.fields.map(x => this.fields.push(new DatasetDescriptionFieldEditorModel().fromModel(x))); }
		this.ordinal = item.ordinal;
		this.id = item.id;
		this.title = item.title;
		this.numbering = item.numbering;
		this.description = item.description;
		this.extendedDescription = item.extendedDescription;
		this.additionalInformation = item.additionalInformation;
		this.hasCommentField = item.hasCommentField;
		this.commentFieldValue = item.commentFieldValue;
		if (item.multiplicity) this.multiplicity = new DatasetDescriptionMultiplicityEditorModel().fromModel(item.multiplicity);
		if (item.multiplicityItems) { item.multiplicityItems.map(x => this.multiplicityItems.push(new DatasetDescriptionCompositeFieldEditorModel().fromModel(x))); }
		return this;
	}

	buildForm(): FormGroup {
		const formGroup = this.formBuilder.group({
			id: this.id,
			ordinal: this.ordinal,
			extendedDescription: this.extendedDescription,
			additionalInformation: this.additionalInformation,
			hasCommentField: this.hasCommentField,
			commentFieldValue: this.commentFieldValue,
			multiplicity: [{ value: this.multiplicity, disabled: true }],
			title: [{ value: this.title, disabled: true }],
			description: [{ value: this.description, disabled: true }],
			numbering: [{ value: this.numbering, disabled: true }],
		});

		const fieldsFormArray = new Array<FormGroup>();
		this.fields.forEach(item => {
			const form: FormGroup = item.buildForm();
			fieldsFormArray.push(form);
		});
		formGroup.addControl('fields', this.formBuilder.array(fieldsFormArray));

		const multiplicityItemsFormArray = new Array<FormGroup>();
		this.multiplicityItems.forEach(item => {
			const form: FormGroup = item.buildForm();
			multiplicityItemsFormArray.push(form);
		});
		formGroup.addControl('multiplicityItems', this.formBuilder.array(multiplicityItemsFormArray));

		return formGroup;
	}

	// cloneForMultiplicity(index: number): DatasetDescriptionCompositeFieldEditorModel {
	// 	const newItem: DatasetDescriptionCompositeFieldEditorModel = new DatasetDescriptionCompositeFieldEditorModel();
	// 	newItem.id = 'multiple_' + this.id + '_' + index;
	// 	this.fields.forEach(field => {
	// 		newItem.fields.push(field.cloneForMultiplicity(this.fields.indexOf(field), newItem.id + '_'));
	// 	});
	// 	newItem.ordinal = this.ordinal;
	// 	return newItem;
	// }

	cloneForMultiplicity(item: CompositeField, ordinal: number, idMappings: { old: string, new: string }[] = []): DatasetDescriptionCompositeFieldEditorModel {
		const newItem: DatasetDescriptionCompositeFieldEditorModel = new DatasetDescriptionCompositeFieldEditorModel();
		newItem.id = 'multiple_' + item.id + '_' + Guid.create() + '_' + ordinal;

		idMappings.push({ old: item.id, new: newItem.id });
		item.fields.forEach((field, index) => {

			const clonedItem = new DatasetDescriptionFieldEditorModel().cloneForMultiplicity(field, newItem.id)
			newItem.fields.push(clonedItem);

			idMappings.push({ old: field.id, new: clonedItem.id });
		});
		newItem.ordinal = ordinal;
		return newItem;
	}
}

export class DatasetDescriptionFieldEditorModel extends BaseFormModel {

	public id: string;
	public title: string;
	public value: any;
	public defaultValue: DefaultValue;
	public description: string;
	public numbering: string;
	public extendedDescription: string;
	public additionalInformation: string;
	public viewStyle: ViewStyle;
	public defaultVisibility: boolean;
	public page: number;
	public multiplicity: DatasetDescriptionMultiplicityEditorModel;
	public multiplicityItems: Array<DatasetDescriptionFieldEditorModel> = new Array<DatasetDescriptionFieldEditorModel>();
	public data: any;
	public validations: Array<ValidationType>;
	public validationRequired = false;
	public validationURL = false;
	public ordinal: number;

	fromModel(item: Field): DatasetDescriptionFieldEditorModel {
		this.id = item.id;
		this.ordinal = item.ordinal;
		this.title = item.title;
		this.numbering = item.numbering;
		this.description = item.description;
		this.extendedDescription = item.extendedDescription;
		this.additionalInformation = item.additionalInformation;
		this.viewStyle = item.viewStyle;
		this.defaultVisibility = item.defaultVisibility;
		this.page = item.page;
		this.validations = item.validations;
		if (item.multiplicity) this.multiplicity = new DatasetDescriptionMultiplicityEditorModel().fromModel(item.multiplicity);
		if (item.defaultValue) this.defaultValue = new DatasetDescriptionDefaultValueEditorModel().fromModel(item.defaultValue);
		this.value = item.value ? item.value : (this.defaultValue.value ? this.defaultValue.value : undefined);
		if (this.viewStyle.renderStyle === 'checkBox' && (item.value !== true)) { this.value = this.value === 'true'; } //Cover both posibilites of boolean true or false and string 'true' or 'false'
		if (item.multiplicityItems) { item.multiplicityItems.map(x => this.multiplicityItems.push(new DatasetDescriptionFieldEditorModel().fromModel(x))); }
		this.data = item.data;
		return this;
	}

	buildForm(): FormGroup {
		if (this.validations) {
			this.validations.forEach(validation => {
				if (validation === ValidationType.Required) {
					this.validationRequired = true;
				} else if (validation === ValidationType.URL) {
					this.validationURL = true;
				}
			});
		}
		let validators: any[] = [];
		if (this.validationRequired) {
			validators.push(Validators.required)
		}
		if (this.validationURL) {
			validators.push(ValidatorURL.validator);
		}
		const formGroup = this.formBuilder.group({
			value: [this.value, validators],
			id: [{ value: this.id, disabled: false }],
			viewStyle: [{ value: this.viewStyle, disabled: true }],
			data: [{ value: this.data, disabled: true }],
			validationRequired: [{ value: this.validationRequired, disabled: true }],
			validationURL: [{ value: this.validationURL, disabled: true }],
			description: [{ value: this.description, disabled: true }],
			extendedDescription: [{ value: this.extendedDescription, disabled: true }],
			additionalInformation: [{ value: this.additionalInformation, disabled: true }],
			title: [{ value: this.title, disabled: true }],
			defaultValue: [{ value: this.defaultValue, disabled: true }],
			ordinal: [{ value: this.ordinal, disabled: true }]
		});

		const multiplicityItemsFormArray = new Array<FormGroup>();
		this.multiplicityItems.forEach(item => {
			const form: FormGroup = item.buildForm();
			multiplicityItemsFormArray.push(form);
		});
		formGroup.addControl('multiplicityItems', this.formBuilder.array(multiplicityItemsFormArray));

		return formGroup;
	}

	// cloneForMultiplicity(index: number, idPath: string): DatasetDescriptionFieldEditorModel {
	// 	const newItem: DatasetDescriptionFieldEditorModel = new DatasetDescriptionFieldEditorModel();

	// 	newItem.id = idPath ? idPath + index : 'multiple_' + this.id + '_' + index;
	// 	newItem.title = this.title;
	// 	newItem.description = this.description;
	// 	newItem.extendedDescription = this.extendedDescription;
	// 	newItem.viewStyle = this.viewStyle;
	// 	newItem.defaultVisibility = this.defaultVisibility;
	// 	newItem.page = this.page;
	// 	newItem.multiplicity = null;
	// 	newItem.data = this.data;

	// 	return newItem;
	// }
	cloneForMultiplicity(item: Field, idPath: string): DatasetDescriptionFieldEditorModel {
		const newItem: DatasetDescriptionFieldEditorModel = new DatasetDescriptionFieldEditorModel();

		//newItem.id = idPath ? idPath  : 'multiple_' + item.id + '_' + Guid.create();
		newItem.id = idPath ? idPath + '_' + item.id : 'multiple_' + Guid.create() + '_' + item.id;
		newItem.title = item.title;
		newItem.description = item.description;
		newItem.extendedDescription = item.extendedDescription;
		newItem.additionalInformation = item.additionalInformation;
		newItem.viewStyle = item.viewStyle;
		newItem.defaultVisibility = item.defaultVisibility;
		newItem.page = item.page;
		newItem.multiplicity = null;
		newItem.data = item.data;

		return newItem;
	}
}


export class DatasetDescriptionMultiplicityEditorModel extends BaseFormModel {
	public min: number;
	public max: number;
	public placeholder: string;

	fromModel(item: Multiplicity): DatasetDescriptionMultiplicityEditorModel {
		this.min = item.min;
		this.max = item.max;
		this.placeholder = item.placeholder;
		return this;
	}

	buildForm(): FormGroup {
		const formGroup = this.formBuilder.group({
			min: [this.min],
			max: [this.max],
			placeholder: [this.placeholder]
		});
		return formGroup;
	}
}

export class DatasetDescriptionDefaultValueEditorModel extends BaseFormModel {
	public type: string;
	public value: string;

	fromModel(item: DefaultValue): DatasetDescriptionDefaultValueEditorModel {
		this.type = item.type;
		this.value = item.value;
		return this;
	}

	buildForm(): FormGroup {
		const formGroup = this.formBuilder.group({
			type: [this.type],
			value: [this.value]
		});
		return formGroup;
	}
}
