import { trigger } from '@angular/animations';
import { Component, OnInit } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { ActivatedRoute, ActivationEnd, Router } from '@angular/router';
import { ToastrService } from 'ngx-toastr';
import { Observable, Subscription } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
import { ProductSpecification, ProductSpecificationCreate, ProductSpecificationUpdate, ServiceSpecificationRef, ProductSpecificationCharacteristicRes } from 'src/app/openApis/productCatalogManagement/models';
import { ProductSpecificationService } from 'src/app/openApis/productCatalogManagement/services';
import { fadeIn } from 'src/app/shared/animations/animations';
import { AppService } from 'src/app/shared/services/app.service';
import { AssignServiceSpecificationComponent } from './assign-service-specification/assign-service-specification.component';
import { MatTableDataSource } from '@angular/material/table';
import { EditProductSpecCharacteristicsComponent } from './edit-product-spec-characteristics/edit-product-spec-characteristics.component';
import { DeleteProductSpecCharacteristicsComponent } from './delete-product-spec-characteristics/delete-product-spec-characteristics.component';

@Component({
  selector: 'app-edit-product-specs',
  templateUrl: './edit-product-specs.component.html',
  styleUrls: ['./edit-product-specs.component.scss'],
  animations: [ trigger('fadeIn', fadeIn()) ]
})
export class EditProductSpecsComponent implements OnInit {

  // to change
  displayedColumnsCharacteristics = ['name', 'type', 'defaultValues', 'configurable', 'actions'];
  specCharacteristicsTags: string[] = ["All"]
  tagFiltervalue:string = "All"
  dataSource = new MatTableDataSource<ProductSpecificationCharacteristicRes>();
  

  constructor(
    private activatedRoute: ActivatedRoute,
    private specService: ProductSpecificationService,
    private dialog: MatDialog,
    private toast: ToastrService,
    private router: Router,
    public appService: AppService
  ) { }

  specID: string
  spec: ProductSpecification
  specNotFound: boolean = false
  finishedLoading: boolean = false
  newSpecification = false

  listItems = ["Main Properties", "Service Specification Relationships", "Related Parties", "Product Specification Characteristics"]
  activeListItem = "Main Properties"

  editForm =  new UntypedFormGroup({
    description: new UntypedFormControl(),
    lifecycleStatus: new UntypedFormControl("In design", Validators.required),
    name: new UntypedFormControl(null, Validators.required),
    // isBundle: new FormControl(),
    validFor: new UntypedFormGroup({
      endDateTime: new UntypedFormControl(new Date(new Date().setFullYear(new Date().getFullYear()+20)), Validators.required),
      startDateTime: new UntypedFormControl(new Date(), Validators.required)
    }),
    version: new UntypedFormControl("0.1.0", Validators.required)
  })
  lifecycleStatuses = ["In study", "In design", "In test", "Active", "Launched", "Retired", "Obsolete", "Rejected"]

  serviceSpecificationRelationshipFilterCtrl = new UntypedFormControl();
  filteredServiceSpecificationRel$: Observable<ServiceSpecificationRef[]>

  subscriptions = new Subscription()


  ngOnInit(): void {
    this.initSubscriptions()

    if (this.activatedRoute.snapshot.params.id)
    {
      this.specID = this.activatedRoute.snapshot.params.id
      this.retrieveProductSpec()
    } else {
      this.newSpecification = true
      this.finishedLoading = true
    }
  }

  initSubscriptions() {
    this.subscriptions.add(this.router.events.subscribe(
      event => {
        if (event instanceof ActivationEnd && event.snapshot.params && event.snapshot.params.id) {
          this.specID = event.snapshot.params.id
          this.retrieveProductSpec()
        }
      }
    ))
  }

  selectListitem(item: string) {
    if (this.editForm.pristine) {
      this.activeListItem = item
    } else {
      const dialogRef = this.dialog.open(DiscardChangesComponent, {autoFocus: true})

      dialogRef.afterClosed().subscribe (discardChanges => {
        if (discardChanges) {
          this.editForm.patchValue(this.spec)
          this.editForm.markAsPristine()
          this.activeListItem = item
        }
      })
    }
  }

  retrieveProductSpec() {
    this.specService.retrieveProductSpecification({id: this.specID}).subscribe(
      data => this.spec = data,
      error => this.toast.error("An error occurred while loading Product Specification"),
      () => {
        if (this.spec) {
          this.finishedLoading = true
          //populate General Panel Info
          if (!this.spec.validFor) this.spec.validFor = { endDateTime: new Date(new Date().setFullYear(new Date().getFullYear()+20)).toISOString(), startDateTime: new Date().toISOString() }
          this.editForm.patchValue(this.spec)
          this.editForm.markAsPristine()
          
          //populate Service Specification Relationship Panel Info
          this.filteredServiceSpecificationRel$ = this.serviceSpecificationRelationshipFilterCtrl.valueChanges.pipe(
            startWith(null),
            map( (value:null | string) => value ? this._filterOnRelatedSpecs(value) : this.spec.serviceSpecification.slice() )
          )

          //populate Specification Characteristic Panel Info
          this.dataSource.data = this.spec.productSpecCharacteristic;
          // this.dataSource.paginator = this.paginator;

          this.specCharacteristicsTags = ["All"]
          this.tagFiltervalue = "All"
          this.specCharacteristicsTags = this.retrieveSpecCharaceristicsTags(this.dataSource.data)

  
        }
        else {
          this.specNotFound = true
        }
      }
    )
  }

  retrieveSpecCharaceristicsTags(dataSource: ProductSpecificationCharacteristicRes[]) {
      let tagsArray = this.specCharacteristicsTags
      dataSource.forEach(char => {
        char.productSpecCharRelationship.filter( e => e.relationshipType === "tag").forEach(rel => {
          if (!tagsArray.includes(rel.name)) tagsArray.push(rel.name)
        })
      });
      return tagsArray
    }

  private _filterOnRelatedSpecs(filterValue: string) {
    filterValue = filterValue.trim();
    filterValue = filterValue.toLowerCase();
    return this.spec.serviceSpecification.filter( relatedSpec =>  relatedSpec.name.toLowerCase().includes(filterValue) )
  }

  openAssignSpecRelationshipDialog() {
    const dialogRef = this.dialog.open(AssignServiceSpecificationComponent, {
      data: {
        productSpec: this.spec
      },
      autoFocus: false,
      disableClose: false
    })

    dialogRef.afterClosed().subscribe (
      result => { 
        if (result) { 
          this.toast.success("Product Specification Relationship list was successfully updated")
          this.retrieveProductSpec()
        }
      }
    )
  }

  updateProductSpecGeneral() {
    if (this.editForm.valid) {
      const updateObj: ProductSpecificationCreate | ProductSpecificationUpdate = {
        description: this.editForm.value.description,
        lifecycleStatus: this.editForm.value.lifecycleStatus,
        name: this.editForm.value.name,
        validFor: this.editForm.value.validFor,
        version: this.editForm.value.version
      }
  
      let updatedSpec: ProductSpecification
  
      if (this.newSpecification) {
        this.specService.createProductSpecification(updateObj).subscribe(
          data => { updatedSpec = data },
          error => this.toast.error("An error occurred while creating the Product Specification"),
          () => {
            this.newSpecification = false
            this.toast.success("Product Specification was successfully created")
            this.refreshProductSpecification(updatedSpec)
          }
        )
      }
      else {
        this.specService.patchProductSpecification({ id: this.specID, productSpecification: updateObj }).subscribe(
          data => { updatedSpec = data },
          error => this.toast.error("An error occurred while updating the Product Specification"),
          () => {
            this.toast.success("Product Specification was successfully updated")
            this.refreshProductSpecification(updatedSpec)
          }
        )
      }
    }
    
  } 

  refreshProductSpecification(updatedSpec : ProductSpecification) {
    this.specID = updatedSpec.id
    this.retrieveProductSpec()
  }


  cloneProductSpecCharacteristic(characteristic: ProductSpecificationCharacteristicRes) {
    const cloneCharacteristic: ProductSpecificationCharacteristicRes = {
      name: `Copy of ${characteristic.name}`,
      description: characteristic.description,
      configurable: characteristic.configurable,
      extensible: characteristic.extensible,
      minCardinality: characteristic.minCardinality,
      maxCardinality: characteristic.maxCardinality,
      productSpecCharRelationship: characteristic.productSpecCharRelationship,
      productSpecCharacteristicValue: characteristic.productSpecCharacteristicValue,
      validFor: characteristic.validFor,
      valueType: characteristic.valueType
    }

    this.spec.productSpecCharacteristic.push(cloneCharacteristic);

    const updateCharacteristicObj: ProductSpecificationUpdate = {
      productSpecCharacteristic: this.spec.productSpecCharacteristic
    }

    this.specService.patchProductSpecification({id: this.spec.id, productSpecification: updateCharacteristicObj}).subscribe(
      data => {},
      error => this.toast.error("An error occurred while cloning the Product Specification Characteristic"),
      () => {
        this.toast.success("Product Specification Characteristics list was successfully updated");
        this.retrieveProductSpec();
      }
    )
  }

  applySpecCharFilter(filterValue: string) {
    filterValue = filterValue.trim();
    filterValue = filterValue.toLowerCase();
    this.dataSource.filter = filterValue;
  }

  openCharacteristicDeleteDialog(characteristic: ProductSpecificationCharacteristicRes) {
    const specToBeDeletedIndex = this.spec.productSpecCharacteristic.findIndex(char => char.uuid === characteristic.uuid)

    const newSpecCharacteristicArray: ProductSpecificationCharacteristicRes[] = this.spec.productSpecCharacteristic.slice()

    newSpecCharacteristicArray.splice(specToBeDeletedIndex, 1)

    const dialogRef = this.dialog.open(DeleteProductSpecCharacteristicsComponent, {
      data: {
        productSpec: this.spec,
        productSpecCharacteristicArray: newSpecCharacteristicArray,
        specToBeDeleted: this.spec.productSpecCharacteristic[specToBeDeletedIndex]
      }
    })

    dialogRef.afterClosed().subscribe (
      result => { 
        if (result){ 
          this.toast.success("Product Specification Characteristics list was successfully updated");
          this.retrieveProductSpec()
        }
      }
    )
  }

  openCharacteristicDesignDialog(characteristic: ProductSpecificationCharacteristicRes) {
    const dialogRef = this.dialog.open(EditProductSpecCharacteristicsComponent, {
      data: {
        productSpec: this.spec,
        specToBeUpdated: characteristic
      },
      disableClose: true
    })

    dialogRef.afterClosed().subscribe (
      result => { 
        if (result) { 
          this.toast.success("Product Specification Characteristics list was successfully updated");
          this.retrieveProductSpec();
        }
      }
    )
  }

  filterCharacteristicsByTag(tagName) {
    this.tagFiltervalue = tagName
    if (tagName === "All") {
      this.dataSource.data = this.spec.productSpecCharacteristic.filter(specCharacteristic => specCharacteristic.valueType);
    } else {
      this.dataSource.data = this.spec.productSpecCharacteristic.filter(specCharacteristic => specCharacteristic.valueType)
      .filter(specChar => specChar.productSpecCharRelationship.some( rel => rel.name === tagName ));
    }
  }

  ngOnDestroy() {
    this.subscriptions.unsubscribe()
  }

}

@Component({
  selector: 'app-discard-changes',
  templateUrl: 'discard-changes.component.html',
})

export class DiscardChangesComponent {

  constructor(
    public dialogRef: MatDialogRef<DiscardChangesComponent>,
  ) {}

  onNoClick(): void {
    this.dialogRef.close(false);
  }

  onYesClick(): void {
    this.dialogRef.close(true)
  }
}

