import {
	ChangeDetectionStrategy,
	ChangeDetectorRef,
	Component,
	EventEmitter,
	Input,
	OnChanges,
	OnInit,
	Output,
	SimpleChanges
} from '@angular/core';
import {FormArray, FormGroup} from '@angular/forms';
import {Rule} from '@app/core/model/dataset-profile-definition/rule';
import {BaseComponent} from '@common/base/base.component';
import {takeUntil} from 'rxjs/operators';
import {DatasetDescriptionCompositeFieldEditorModel} from '../../dataset-description-form.model';
import {ToCEntry, ToCEntryType} from '../../dataset-description.component';
import {FormFocusService} from '../../form-focus/form-focus.service';
import {LinkToScroll} from '../../tableOfContentsMaterial/table-of-contents';
import {VisibilityRuleSource} from '../../visibility-rules/models/visibility-rule-source';
import {VisibilityRulesService} from '../../visibility-rules/visibility-rules.service';


@Component({
	selector: 'app-form-section',
	templateUrl: './form-section.component.html',
	styleUrls: ['./form-section.component.scss'],
	changeDetection: ChangeDetectionStrategy.OnPush
})
export class FormSectionComponent extends BaseComponent implements OnInit, OnChanges {

	//@Input() section: DatasetDescriptionSectionEditorModel;
	@Input() datasetProfileId: String;
	@Input() form: FormGroup;
	@Input() tocentry: ToCEntry;
	@Input() pathName: string;
	@Input() path: string;
	@Input() linkToScroll: LinkToScroll;
	@Input() hiddenEntriesIds: string[] = [];
	//trackByFn = (index, item) => item ? item['id'] : null;
	panelExpanded = true;
	// sub = true;
	subsectionLinkToScroll: LinkToScroll;


	@Output() askedToScroll = new EventEmitter<string>();

	tocentriesType = ToCEntryType;
	@Input() TOCENTRY_ID_PREFIX = "";

	constructor(
		public visibilityRulesService: VisibilityRulesService,
		private formFocusService: FormFocusService,
		private changeDetector: ChangeDetectorRef
	) {
		super();
		this.visibilityRulesService.getElementVisibilityMapObservable().pipe(takeUntil(this._destroyed)).subscribe(x => {
			this.changeDetector.markForCheck();
		});
	}

	ngOnInit() {
		// if (this.section) {
		// 	this.form = this.visibilityRulesService.getFormGroup(this.section.id);
		// }

		if (this.tocentry) {//maybe not needed as well
			this.form = this.tocentry.form as FormGroup;
		}
		this.initMultipleFieldsVisibility();
	}

	ngOnChanges(changes: SimpleChanges) {

		if (changes['linkToScroll']) {
			if (changes['linkToScroll'].currentValue && changes['linkToScroll'].currentValue.section) {

				if (this.pathName === changes['linkToScroll'].currentValue.section) {
					this.panelExpanded = true;
				} else if (changes['linkToScroll'].currentValue.section.includes(this.pathName)) {
					this.subsectionLinkToScroll = changes['linkToScroll'].currentValue;
					this.panelExpanded = true;
				}
			}
		}
	}

	// ngAfterViewInit() {
	// 	this.visibilityRulesService.triggerVisibilityEvaluation();
	// }

	setMultipleFieldVisibility(parentCompositeField, compositeField: DatasetDescriptionCompositeFieldEditorModel, idMappings: { old: string, new: string }[]) {
		// ** COMPOSITE FIELD IS SHOWN OR HIDDEN FROM ANOTHER ELEMENT
		const compositeFieldVisibilityDependencies = this.visibilityRulesService.getVisibilityDependency(parentCompositeField);
		if (compositeFieldVisibilityDependencies && compositeFieldVisibilityDependencies.length) {

			compositeFieldVisibilityDependencies.forEach(x => {
				const visRule: Rule = {
					targetField: compositeField.id,
					sourceField: x.sourceControlId,
					requiredValue: x.sourceControlValue,
					type: ''
				}
				this.visibilityRulesService.addNewRule(visRule);
			});
		}

		// console.log('idMappings ', idMappings);
		parentCompositeField.fields.forEach(element => {
			// console.log(this.visibilityRulesService.getVisibilityDependency(element.id));
			const dependency = this.visibilityRulesService.getVisibilityDependency(element.id);
			if (dependency && dependency.length) {
				// * INNER VISIBILITY DEPENDENCIES
				// * IF INNER INPUT HIDES ANOTHER INNER INPUT
				const innerDependency = parentCompositeField.fields.reduce((innerD, currentElement) => {
					const innerDependecies = dependency.filter(d => d.sourceControlId === currentElement.id);
					return [...innerD, ...innerDependecies];
				}, []) as VisibilityRuleSource[];
				if (innerDependency.length) {
					//Build visibility source
					const updatedRules = innerDependency.map(x => {
						const newId = idMappings.find(y => y.old === x.sourceControlId);
						return {...x, sourceControlId: newId.new};
					});
					// const visRule: VisibilityRule = {
					// 	targetControlId: idMappings.find(x => x.old === element.id).new,
					// 	sourceVisibilityRules: updatedRules
					// }


					const rules = updatedRules.map(x => {
						return {
							requiredValue: x.sourceControlValue,
							sourceField: x.sourceControlId,
							targetField: idMappings.find(l => l.old === element.id).new,
							type: ''
						} as Rule;
					});

					rules.forEach(rule => {
						this.visibilityRulesService.addNewRule(rule);
					})

					// this.visibilityRulesService.appendVisibilityRule(visRule);
				}

			}


			// * OUTER DEPENDENCIES

			// * IF INNER INPUT HIDES OUTER INPUTS
			const innerIds = idMappings.map(x => x.old) as string[];

			const outerTargets = this.visibilityRulesService.getVisibilityTargets(element.id).filter(x => !innerIds.includes(x));

			outerTargets.forEach(target => {

				const outerRules = (this.visibilityRulesService.getVisibilityDependency(target) as VisibilityRuleSource[]).filter(x => x.sourceControlId === element.id);
				const updatedRules = outerRules.map(x => {
					console.log(idMappings, element);
					return {...x, sourceControlId: idMappings.find(y => y.old === element.id).new};
				});

				// const visRule: VisibilityRule = {
				// 	targetControlId: target,
				// 	sourceVisibilityRules: updatedRules
				// }


				const rules = updatedRules.map(x => {
					return {
						requiredValue: x.sourceControlValue,
						sourceField: x.sourceControlId,
						targetField: target,
						type: ''
					} as Rule;
				})
				rules.forEach(rule => {
					this.visibilityRulesService.addNewRule(rule);
				})
				// this.visibilityRulesService.appendVisibilityRule(visRule);
			});
			// * IF INNER INPUT IS HIDDEN BY OUTER INPUT
			if (dependency && dependency.length) {
				const fieldsThatHideInnerElement = dependency.filter(x => !innerIds.includes(x.sourceControlId));
				if (fieldsThatHideInnerElement.length) {
					fieldsThatHideInnerElement.forEach(x => {
						const visRule: Rule = {
							targetField: idMappings.find(l => l.old === element.id).new,
							sourceField: x.sourceControlId,
							requiredValue: x.sourceControlValue,
							type: ''
						}
						const shouldBeVisibile = this.visibilityRulesService.checkTargetVisibilityProvidedBySource(x.sourceControlId, element.id);
						this.visibilityRulesService.addNewRule(visRule, shouldBeVisibile);
					});
				}
			}
			// console.log(`for ${element.id} targets are`, outerTargets);
		});
	}

	initMultipleFieldsVisibility() {
		(this.form.get('compositeFields') as FormArray).controls.forEach(control => {
			let parentCompositeField = (control as FormGroup).getRawValue();
			if (parentCompositeField.multiplicityItems && parentCompositeField.multiplicityItems.length > 0) {
				parentCompositeField.multiplicityItems.forEach(compositeField => {
					let idMappings: { old: string, new: string }[] = [{old: parentCompositeField.id, new: compositeField.id}];
					parentCompositeField.fields.forEach((field, index) => {
						idMappings.push({ old: field.id, new: compositeField.fields[index].id });
					});
					this.setMultipleFieldVisibility(parentCompositeField, compositeField, idMappings);
				})
			}
		});
	}

	addMultipleField(fieldsetIndex: number) {
		const compositeFieldToBeCloned = (this.form.get('compositeFields').get('' + fieldsetIndex) as FormGroup).getRawValue();
		const multiplicityItemsArray = (<FormArray>(this.form.get('compositeFields').get('' + fieldsetIndex).get('multiplicityItems')));

		const ordinal = multiplicityItemsArray.length ? multiplicityItemsArray.controls.reduce((ordinal, currentControl) => {
			const currentOrdinal = currentControl.get('ordinal').value as number;

			if (currentOrdinal >= ordinal) {
				return currentOrdinal + 1;
			}
			return ordinal as number;
		}, 0) : 0;
		const idMappings = [] as { old: string, new: string }[];
		const compositeField: DatasetDescriptionCompositeFieldEditorModel = new DatasetDescriptionCompositeFieldEditorModel().cloneForMultiplicity(compositeFieldToBeCloned, ordinal, idMappings);

		this.setMultipleFieldVisibility(compositeFieldToBeCloned, compositeField, idMappings);
		multiplicityItemsArray.push(compositeField.buildForm());
	}

	deleteCompositeFieldFormGroup(compositeFildIndex: number) {
		const numberOfItems = this.form.get('compositeFields').get('' + compositeFildIndex).get('multiplicityItems').get('' + 0).get('fields').value.length;
		for (let i = 0; i < numberOfItems; i++) {
			const multiplicityItem = this.form.get('compositeFields').get('' + compositeFildIndex).get('multiplicityItems').get('' + 0).get('fields').get('' + i).value;
			this.form.get('compositeFields').get('' + compositeFildIndex).get('fields').get('' + i).patchValue(multiplicityItem);
		}
		(<FormArray>(this.form.get('compositeFields').get('' + compositeFildIndex).get('multiplicityItems'))).removeAt(0);
	}

	deleteMultipeFieldFromCompositeFormGroup(compositeFildIndex: number, fildIndex: number) {
		const multiplicityItemsArray = (<FormArray>(this.form.get('compositeFields').get('' + compositeFildIndex).get('multiplicityItems')));
		multiplicityItemsArray.removeAt(fildIndex);
		multiplicityItemsArray.controls.forEach((control, i) => {
			try {
				control.get('ordinal').setValue(i);
			} catch {
				throw 'Could not find ordinal';
			}
		});
	}

	// isElementVisible(fieldSet: CompositeField): boolean {
	// 	return fieldSet && fieldSet.fields && fieldSet.fields.length > 0
	// }

	// next(compositeField: CompositeField) {
	// 	this.formFocusService.focusNext(compositeField);
	// }


	onAskedToScroll(id: string) {
		this.panelExpanded = true;
		this.askedToScroll.emit(id);
	}
}
