import { Component, OnInit } from '@angular/core';
import { DetailsFormBase } from '@objects-view/details-form-base';
import { UntypedFormBuilder, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { ConfigService } from '@shared/config/config.service';
import { Observable, of } from 'rxjs';
import { IActionResult } from '@objects-view/interfaces/actions/action-result';
import { IConfigOptionModel } from '@fleio-api/billing/model/config-option.model';
import { ConfigurableOptionsApiService } from '@fleio-api/billing/configurable-options/configurable-option-api.service';
import {
  IConfigurableOptionCreateOptionsModel
} from '@fleio-api/billing/model/configurable-option-create-options.model';
import { IAction } from '@objects-view/interfaces/actions/action';
import { MatDialog } from '@angular/material/dialog';
import { RefreshService } from '@shared/ui-api/refresh.service';
import { IConfigurableOptionCycleModel } from '@fleio-api/billing/model/configurable-option-cycle.model';
import {
  ConfigurableOptionsCycleApiService
} from '@fleio-api/billing/configurable-options/configurable-option-cycle-api.service';
import { CallbackAction } from '@objects-view/actions/callback-action';
import { map } from 'rxjs/operators';
import {
  EditOptionCycleDialogComponent
} from '../../dialogs/edit-option-cycle-dialog/edit-option-cycle-dialog.component';
import { IConfigurableOptionChoiceModel } from '@fleio-api/billing/model/configurable-option-choice.model';
import {
  EditOptionChoiceDialogComponent
} from '../../dialogs/edit-option-choice-dialog/edit-option-choice-dialog.component';
import {
  ConfigurableOptionsChoiceApiService
} from '@fleio-api/billing/configurable-options/configurable-option-choice-api.service';
import { NotificationService } from '@shared/ui-api/notification.service';
import { ServicesApiService } from '@fleio-api/billing/services/service-api.service';
import { ApiCallAction, CallType } from '@objects-view/actions/api-call-action';
import { ValidatorsHelper } from '@shared/ui-api/helpers/validators-helper';

@Component({
  selector: 'app-configurable-option-edit-form',
  templateUrl: './configurable-option-edit-form.component.html',
  styleUrls: ['./configurable-option-edit-form.component.scss']
})
export class ConfigurableOptionEditFormComponent extends DetailsFormBase<IConfigOptionModel> implements OnInit {
  settingsOptions = {
    num_in: {
      min: {
        display: $localize`Minimum value`,
        type: 'number',
        default: 0,
        validators: [Validators.required, ValidatorsHelper.requiredNumericValueValidator, Validators.min(0), Validators.max(2147483647)],
      },
      max: {
        display: $localize`Maximum value`,
        type: 'number',
        default: 2147483647,
        validators: [Validators.required, ValidatorsHelper.requiredNumericValueValidator, Validators.min(1), Validators.max(2147483647)],
      },
      step: {
        display: $localize`Step value`,
        type: 'number',
        default: 1,
        validators: [Validators.required, ValidatorsHelper.requiredNumericValueValidator, Validators.min(1), Validators.max(2147483647)],
      },
    }
  };
  configurableOptionForm = this.formBuilder.group({
    name: ['', Validators.required],
    display_name: ['', Validators.required],
    help_text: [''],
    widget: ['', Validators.required],
    status: ['', Validators.required],
    billing_type: ['', Validators.required],
    default_value: [''],
    required: [false],
    settings: ['{}'],
  });
  createOptions: IConfigurableOptionCreateOptionsModel;
  cycleActions: { [key: string]: IAction[] };
  hasChoices = false;
  choices: IConfigurableOptionChoiceModel[];
  choiceActions: { [key: string]: IAction[] };
  formWidget = this.configurableOptionForm.controls.widget;

  constructor(
    private formBuilder: UntypedFormBuilder,
    private configurableOptionsApiService: ConfigurableOptionsApiService,
    private router: Router,
    public config: ConfigService,
    private matDialog: MatDialog,
    private refreshService: RefreshService,
    private configurableOptionsCycleApiService: ConfigurableOptionsCycleApiService,
    private configurableOptionsChoiceApiService: ConfigurableOptionsChoiceApiService,
    private notificationService: NotificationService,
    private servicesApi: ServicesApiService,
  ) {
    super();
  }

  private updateWidgetSettings(widget: string) {
    // clear old widget settings
    for (const [widgetVal, widgetSettings] of Object.entries(this.settingsOptions)) {
      for (const [settingFieldKey, settingData] of Object.entries(widgetSettings)) {
        if (this.configurableOptionForm.contains(settingFieldKey)) {
          this.configurableOptionForm.removeControl(settingFieldKey);
        }
      }
    }
    // add widget settings if they exist
    if (this.settingsOptions.hasOwnProperty(widget)) {
      for (const [settingFieldKey, settingData] of Object.entries(this.settingsOptions[widget])) {
        this.configurableOptionForm.addControl(settingFieldKey, this.formBuilder.control(
          (settingData as any).default, (settingData as any).validators,
        ));
      }
    }
  }

  ngOnInit() {
    super.ngOnInit();
    if (this.objectController) {
      this.objectController.actionCallback = (action: IAction) => this.configurableOptionActions(action);
      this.createOptions =
        this.objectController.additionalObjects.createOptions as IConfigurableOptionCreateOptionsModel;
      this.objectController.dataChanged$.subscribe(() => {
        this.updateData();
      });
    }

    if (this.object && this.object.id) {
      this.configurableOptionForm.patchValue(this.object);
      this.updateWidgetSettings(this.object.widget);
      if (this.object && this.object.widget === 'yesno' && !this.object.default_value) {
        this.configurableOptionForm.controls.default_value.setValue('no');
      }
      // patch widget settings
      if (this.object?.settings && typeof this.object.settings === 'object') {
        for (const [settingFieldKey, settingValue] of Object.entries(this.object.settings)) {
          if (this.configurableOptionForm.contains(settingFieldKey)) {
            this.configurableOptionForm.controls[settingFieldKey].setValue(settingValue);
          }
        }
      }
      this.updateData();
    }

    this.configurableOptionForm.controls.widget.valueChanges.subscribe(value => {
      if (value === 'yesno') {
        this.configurableOptionForm.controls.default_value.setValue('no');
      } else {
        this.configurableOptionForm.controls.default_value.setValue('');
      }
      this.updateWidgetSettings(value);
    });
  }

  updateData() {
    this.hasChoices = ['drop', 'radio'].includes(this.object.widget);
    this.cycleActions = {};
    this.choiceActions = {};

    if (this.hasChoices) {
      this.choices = this.object.choices;
    } else {
      this.choices = [{
        choice: '',
        label: '',
        cycles: this.object.cycles,
        option: this.object.id
      }];
    }

    let index = 0;
    for (const choice of this.choices) {
      if (choice.cycles) {
        for (const cycle of choice.cycles) {
          this.cycleActions[`${choice.id}_${cycle.id}`] = this.getCycleActions(choice, cycle);
        }
      }

      if (this.hasChoices) {
        this.choiceActions[choice.id] = this.getChoiceActions(choice, index);
      }
      index += 1;
    }
  }

  getCycleActions(choice: IConfigurableOptionChoiceModel, cycle: IConfigurableOptionCycleModel): IAction[] {
    const actions = [];

    let editCycleTooltip = $localize`Edit cycle`;
    if (this.configurableOptionForm.controls.billing_type.value === 'onetime') {
      editCycleTooltip = $localize`Edit price`;
    }

    let deleteCycleTooltip = $localize`Delete cycle`;
    if (this.configurableOptionForm.controls.billing_type.value === 'onetime') {
      deleteCycleTooltip = $localize`Delete price`;
    }
    let cannotDeleteCycleTooltip = $localize`Cannot delete configurable option cycle`;
    if (this.configurableOptionForm.controls.billing_type.value === 'onetime') {
      cannotDeleteCycleTooltip = $localize`Cannot delete configurable option price`;
    }
    let deleteCycleMessage = $localize`Are you sure you want to delete cycle ${cycle.display_name}?`;
    if (this.configurableOptionForm.controls.billing_type.value === 'onetime') {
      deleteCycleMessage = $localize`Are you sure you want to delete price?`;
    }

    actions.push(new CallbackAction(
      {
        object: this.object,
        icon: {name: 'edit'},
        tooltip: editCycleTooltip,
        name: editCycleTooltip,
        refreshAfterExecute: true,
        callback: () => this.matDialog.open(
          EditOptionCycleDialogComponent, {
            data: {
              createOptions: this.createOptions,
              option: this.object,
              cycle,
              choice,
            },
            autoFocus: false,
          }).afterClosed().pipe(map(() => null))
      }
    ));

    actions.push(new CallbackAction(
      {
        object: cycle,
        icon: {name: 'delete'},
        tooltip: deleteCycleTooltip,
        name: deleteCycleTooltip,
        callback: () => {
          this.servicesApi.list({
            filtering: `cycle__cycle:${cycle.cycle}+cycle__cycle_multiplier:${cycle.cycle_multiplier}+configurable_options__option:${this.object.id}`,
          }).subscribe(value => {
            if (value.count) {
              this.notificationService.confirmDialog(
                {
                  title: cannotDeleteCycleTooltip,
                  message: $localize`Operation not permitted. One or more services are using this configurable option cycle.`,
                  yesButtonName: $localize`Ok`,
                  hideNoButton: true,
                }
              );
            } else {
              const dialogResult$ = this.notificationService.confirmDialog({
                title: deleteCycleTooltip,
                message: deleteCycleMessage,
                yesButtonName: $localize`Delete`,
              });
              dialogResult$.subscribe(dialogResult => {
                if (dialogResult?.button === 'yes') {
                  this.configurableOptionsCycleApiService.delete(cycle.id).subscribe({
                    next: () => {
                      this.refreshService.refresh();
                    },
                    error: (error) => {
                      if (error?.error?.detail) {
                        this.notificationService.showMessage(error.error.detail);
                      } else {
                        this.notificationService.showMessage(
                          $localize`Failed to delete configurable option cycle, please check logs for details`
                        );
                      }
                    }
                  });
                }
              });
            }
          });
          return of(null);
        }
      }
    ));

    return actions;
  }

  getChoiceActions(choice: IConfigurableOptionChoiceModel, index: number): IAction[] {
    const actions = [];

    actions.push(new CallbackAction(
      {
        object: this.object,
        icon: {name: 'edit'},
        tooltip: $localize`Edit choice`,
        name: $localize`Edit`,
        refreshAfterExecute: true,
        callback: () => this.matDialog.open(
          EditOptionChoiceDialogComponent, {
            data: {
              option: this.object,
              choice,
            },
            autoFocus: false,
          }).afterClosed().pipe(map(() => null))
      }
    ));

    actions.push(new CallbackAction(
      {
        object: this.object,
        icon: {name: 'delete'},
        tooltip: $localize`Delete choice`,
        name: $localize`Delete`,
        callback: () => {
          this.servicesApi.list({
            filtering: `configurable_options__option__id:${this.object.id}+configurable_options__option_value:${choice.choice}`
          }).subscribe(value => {
            if (value.count) {
              this.notificationService.confirmDialog(
                {
                  title: $localize`Cannot delete configurable option choice`,
                  message: $localize`Operation not permitted. One or more services are using this configurable option choice.`,
                  yesButtonName: $localize`Ok`,
                  hideNoButton: true,
                }
              );
            } else {
              const dialogResult$ = this.notificationService.confirmDialog({
                title: $localize`Delete choice`,
                message: $localize`Are you sure you want to delete choice ${choice.label}?`,
                yesButtonName: $localize`Delete`,
              });
              dialogResult$.subscribe(dialogResult => {
                if (dialogResult?.button === 'yes') {
                  this.configurableOptionsChoiceApiService.delete(choice.id).subscribe({
                    next: () => {
                      this.notificationService.showMessage($localize`Configurable option choice deleted`);
                      this.refreshService.refresh();
                    },
                    error: (error) => {
                      if (error?.error?.detail) {
                        this.notificationService.showMessage(error.error.detail);
                      } else {
                        this.notificationService.showMessage(
                          $localize`Failed to delete configurable option choice, please check logs for details`
                        );
                      }
                    }
                  });
                }
              });
            }
          });
          return of(null);
        }
      }
    ));

    if (index !== 0) {
      actions.push(new ApiCallAction(
        {
          object: choice,
          icon: {name: 'arrow_upward'},
          tooltip: $localize`Move up`,
          name: $localize`Move up`,
          confirmOptions: {
            confirm: false,
            title: null,
            message: null,
          },
          errorMessage: $localize`Failed to change position`,
          apiService: this.configurableOptionsChoiceApiService,
          callType: CallType.Post,
          apiAction: 'change_position',
          apiParams: {
            choice: choice.id,
            action: 'increase',
          },
          refreshAfterExecute: true,
          redirectAfterExecute: false,
        }
      ));
    }

    if ((this.choices.length - 1) !== index) {
      actions.push(new ApiCallAction(
        {
          object: choice,
          icon: {name: 'arrow_downward'},
          tooltip: $localize`Move down`,
          name: $localize`Move down`,
          confirmOptions: {
            confirm: false,
            title: null,
            message: null,
          },
          errorMessage: $localize`Failed to change position`,
          apiService: this.configurableOptionsChoiceApiService,
          callType: CallType.Post,
          apiAction: 'change_position',
          apiParams: {
            choice: choice.id,
            action: 'decrease',
          },
          refreshAfterExecute: true,
          redirectAfterExecute: false,
        }
      ));
    }

    return actions;
  }

  configurableOptionActions(action: IAction): Observable<IActionResult> {
    const value = this.configurableOptionForm.value as IConfigOptionModel;

    const widgetSettings = {};
    if (this.settingsOptions.hasOwnProperty(value.widget)) {
      for (const [settingFieldKey, settingData] of Object.entries(this.settingsOptions[value.widget])) {
        let newValue = value[settingFieldKey];
        if ((settingData as any).type === 'number') {
          if (!newValue && typeof newValue !== 'number') {
            newValue = (settingData as any).default;
          }
        }
        widgetSettings[settingFieldKey] = newValue;
      }
    }
    value.settings = widgetSettings;

    this.createOrUpdate(
      this.configurableOptionsApiService,
      value,
    ).subscribe((configOption: IConfigOptionModel) => {
      if (action.internalName === 'Create and continue') {
        this.router.navigateByUrl(
          this.config.getPanelUrl(`billing/configurable-options/${configOption.id}`)
        ).catch(() => {
        });
      } else {
        this.notificationService.showMessage($localize`Configurable option saved`);
        this.refreshService.refresh();
        this.configurableOptionForm.markAsPristine();
        this.objectController.loading = false;
      }
    });

    return of(null);
  }

  addCycle(choice: IConfigurableOptionChoiceModel) {
    return this.matDialog.open(
      EditOptionCycleDialogComponent, {
        data: {
          createOptions: this.createOptions,
          option: this.object,
          choice,
        },
        autoFocus: false,
      }).afterClosed().subscribe(result => {
      this.refreshService.refresh();
      if (!result) {
        return;
      }
      return of(null);
    });
  }

  addChoice() {
    return this.matDialog.open(
      EditOptionChoiceDialogComponent, {
        data: {
          option: this.object,
        },
        autoFocus: false,
      }).afterClosed().subscribe(result => {
      this.refreshService.refresh();
      if (!result) {
        return;
      }
      return of(null);
    });
  }
}
