import { IPermissionsModel } from '@fleio-api/base-model/IPermissionsModel';
import { IIcon } from '@shared/ui/common/interfaces/icon';
import { ITitle } from '@objects-view/interfaces/card-data/card-title';
import { IObjectStatus, StatusType, StatusValue } from '@objects-view/interfaces/object-status';
import { ObjectUIServiceBase } from '@objects-view/object-ui-service-base';
import { IAction } from '@objects-view/interfaces/actions/action';
import { RouterLinkAction } from '@objects-view/actions/router-link-action';
import { Router } from '@angular/router';
import { ConfigService } from '@shared/config/config.service';
import { IDataField } from '@objects-view/interfaces/card-data/data-field';
import { ApiCallAction } from '@objects-view/actions/api-call-action';
import { IDetailsTab } from '@objects-view/interfaces/details/details-tab';
import { IServiceModel } from '@fleio-api/billing/model/service.model';
import { ServicesApiService } from '@fleio-api/billing/services/service-api.service';
import {
  ServiceDetailsOverviewComponent
} from '@billing-service-tabs/service-details-overview/service-details-overview.component';
import { CallbackAction } from '@objects-view/actions/callback-action';
import { DateFormatterPipe } from '@shared/ui/common/pipes/date-formatter.pipe';
import { catchError, map } from 'rxjs/operators';
import { IActionResult } from '@objects-view/interfaces/actions/action-result';
import { MatDialog } from '@angular/material/dialog';
import { IYesNoDialogFlag } from '@shared/ui-api/interfaces/yes-no-dialog-flag';
import { Observable, of } from 'rxjs';
import { ServiceChangeFormComponent } from '@billing-service-tabs/service-change-form/service-change-form.component';
import { ServiceDetailsCyclesComponent } from './tabs/service-details-cycles/service-details-cycles.component';
import { ServiceCycleEditTrackerService } from '../service-cycles/service-cycle-edit-tracker.service';
import { IYesNoDialogResult } from '@shared/ui-api/interfaces/yes-no-dialog-result';
import { EntityAuditLogComponent } from '../../common-components/entity-audit-log/entity-audit-log.component';
import { ServiceEditFormComponent } from './tabs/service-edit-form/service-edit-form.component';
import { ServiceEnableBillingComponent } from './dialogs/service-enable-billing/service-enable-billing.component';
import { ServiceChangeProjectComponent } from './dialogs/service-change-project/service-change-project.component';
import { PluginsComponentsRegistryService } from '@shared/plugins/plugins-components-registry.service';

export class ServiceUIService extends ObjectUIServiceBase<IServiceModel> {
  private readonly router: Router;
  private readonly config: ConfigService;
  private readonly servicesApiService: ServicesApiService;
  private readonly matDialog: MatDialog;

  constructor(
    service: IServiceModel, permissions: IPermissionsModel, state: string,
    router: Router, config: ConfigService, servicesApiService: ServicesApiService, matDialog: MatDialog,
    private serviceCycleEditTrackerService: ServiceCycleEditTrackerService,
    private pluginsComponentsRegistry: PluginsComponentsRegistryService,
  ) {
    super(service, permissions, state);
    this.matDialog = matDialog;
    this.router = router;
    this.config = config;
    this.servicesApiService = servicesApiService;
  }

  getIcon(): IIcon {
    return null;
  }

  getStatus(): IObjectStatus {
    switch (this.object.status) {
      case 'active':
        return {type: StatusType.Defined, value: StatusValue.Active};
      case 'pending':
        return {type: StatusType.Defined, value: StatusValue.Pending};
      case 'suspending':
        return {type: StatusType.Changing, value: StatusValue.Suspended};
      case 'suspended':
        return {type: StatusType.Defined, value: StatusValue.Suspended};
      case 'terminated':
        return {type: StatusType.Defined, value: StatusValue.Terminated};
      case 'canceled':
        return {type: StatusType.Defined, value: StatusValue.Canceled};
      case 'deleting':
        return {type: StatusType.Changing, value: StatusValue.Deleted};
      default:
        return {type: StatusType.None, value: StatusValue.None};
    }
  }

  getTitle(): ITitle {
    switch (this.state) {
      case 'edit':
        return {
          text: $localize`Edit ${this.object.display_name}`,
          subText: this.object.status.toLocaleUpperCase(),
        };

      case 'create':
        return {
          text: $localize`Create service`,
        };

      case 'change-options':
        return {
          text: $localize`${this.object.display_name} - change service`,
        };
      case 'change-product':
        return {
          text: $localize`${this.object.display_name} - change product`,
        };
      default:
        let subText: string;
        if (this.object.domain_name) {
          subText = $localize`${this.object.domain_name}, ${this.object.status.toLocaleUpperCase()}`;
        } else {
          subText = this.object.status.toLocaleUpperCase();
        }
        return {
          text: `${this.object.display_name}`,
          subText,
        };
    }
  }

  getActions(): IAction[] {
    const actions: IAction[] = [];

    switch (this.object.status) {
      case 'active':
      case 'suspending':
        actions.push(new CallbackAction({
          icon: {name: 'pause'},
          tooltip: $localize`Suspend service`,
          name: $localize`Suspend service`,
          displayMessages: true,
          confirmOptions: {
            confirm: true,
            title: $localize`Suspend service`,
            message: $localize`Are you sure you want to suspend service ${this.object.display_name} and all associated resources ?`,
            noButtonName: $localize`Cancel`,
            yesButtonName: $localize`Suspend`,
            flags: this.object.suspend_flags,
          },
          callback: (action, result) => {
            if (result?.button === 'yes') {
              return this.servicesApiService.suspend(this.object.id, result.flags).pipe(map(response => {
                return {
                  success: true,
                  message: response.detail,
                } as IActionResult;
              }), catchError((error) => {
                return of({success: false, message: error.error.detail} as IActionResult)
              }));
            } else {
              return of({success: false} as IActionResult);
            }
          },
          refreshAfterExecute: true,
        }));
        break;
      case 'suspended':
        actions.push(new ApiCallAction(
          {
            object: this.object,
            icon: {name: 'play_arrow'},
            tooltip: $localize`Unsuspend service`,
            name: $localize`Unsuspend`,
            confirmOptions: {
              confirm: true,
              title: $localize`Unsuspend service`,
              message: $localize`Are you sure you want to unsuspend service ${this.object.display_name}?`
            },
            apiService: this.servicesApiService,
            apiAction: 'unsuspend',
            refreshAfterExecute: true,
          }
        ));
        break;
      case 'pending':
      case 'terminated':
        actions.push(new ApiCallAction(
          {
            object: this.object,
            icon: {name: 'add'},
            tooltip: $localize`Activate service`,
            name: $localize`Activate`,
            confirmOptions: {
              confirm: true,
              title: $localize`Activate service`,
              message: $localize`Activate service ${this.object.display_name}?`,
            },
            apiService: this.servicesApiService,
            apiAction: 'activate',
            refreshAfterExecute: true,
          }
        ));
        break;
    }

    if (this.object.last_service_cycle_state && (this.object.last_service_cycle_state === 'payment canceled' ||
      this.object.last_service_cycle_state === 'refunded')) {
      actions.push(new CallbackAction(
        {
          object: this.object,
          icon: {name: 'play_arrow'},
          tooltip: $localize`Enable billing`,
          name: $localize`Enable billing`,
          refreshAfterExecute: true,
          callback: () => this.matDialog.open(
            ServiceEnableBillingComponent, {
              data: {service: this.object},
              autoFocus: false,
            }).afterClosed().pipe(map(result => {
            if (result === false) {
              return;
            }
            this.serviceCycleEditTrackerService.editedServiceCycle();
            return {message: result} as IActionResult;
          }))
        }
      ));
    }

    if (this.object.status !== 'terminated') {
      actions.push(new ApiCallAction(
        {
          object: this.object,
          icon: {name: 'cancel'},
          tooltip: $localize`Terminate service`,
          name: $localize`Terminate`,
          confirmOptions: {
            confirm: true,
            title: $localize`Terminate service`,
            message: $localize`Are you sure you want to terminate service ${this.object.display_name}?`,
          },
          apiService: this.servicesApiService,
          apiAction: 'terminate',
          refreshAfterExecute: true,
        }
      ));
    }

    if (['active', 'suspending', 'suspended'].includes(this.object.status)) {
      actions.push(new ApiCallAction(
        {
          object: this.object,
          icon: {name: 'receipt'},
          tooltip: $localize`Issue invoice`,
          name: $localize`Issue invoice`,
          confirmOptions: {
            confirm: true,
            title: $localize`Issue invoice`,
            message: $localize`Issue invoice for service ${this.object.display_name}?`,
          },
          apiService: this.servicesApiService,
          apiAction: 'invoice',
          refreshAfterExecute: true,
          postExecuteCallback: () => {
            // service cycles may have changed, ensure they refresh
            this.serviceCycleEditTrackerService.editedServiceCycle();
          }
        }
      ));
    }

    if (this.object.product.product_type === 'openstack') {
      actions.push(new CallbackAction(
        {
          object: this.object,
          icon: {
            name: 'openstack',
            class: 'fl-icons',
          },
          tooltip: $localize`Change project`,
          name: $localize`Change project`,
          callback: () => this.matDialog.open(
            ServiceChangeProjectComponent, {
              data: {service: this.object},
              autoFocus: false,
            }).afterClosed().pipe(map(result => {
            if (result === false || typeof result === 'undefined') {
              return;
            }
            this.router.navigateByUrl(
              this.config.getPanelUrl('billing/services')
            ).catch();
            return {message: result} as IActionResult;
          }))
        }
      ));
    }

    for (const action of Object.keys(this.object.actions)) {
      const actionDefinition = this.object.actions[action];
      actions.push(new ApiCallAction(
        {
          object: this.object,
          icon: {name: actionDefinition.icon},
          tooltip: actionDefinition.description,
          name: actionDefinition.display_name,
          confirmOptions: {
            confirm: true,
            title: actionDefinition.description,
            message: $localize`Are you sure you want to ${actionDefinition.display_name.toLocaleLowerCase()} service ${this.object.display_name}?`,
          },
          apiService: this.servicesApiService,
          apiAction: 'execute_action',
          apiParams: {
            action
          },
          refreshAfterExecute: true,
        }
      ));
    }

    actions.push(new CallbackAction({
        object: this.object,
        icon: {name: 'delete'},
        tooltip: $localize`Delete service`,
        name: $localize`Delete`,
        redirectAfterExecute: true,
        redirectUrl: this.config.getPanelUrl('billing/services'),
        noPermissions: !this.permissions['services.destroy'],
        callback: action => new Observable<IActionResult>((subscriber) => {
          const flags: IYesNoDialogFlag[] = [];
          let dialogMessage: string;
          let importantMessage: string;

          if (!['pending', 'canceled', 'terminated', 'fraud'].includes(this.object.status)) {
            // noinspection SqlNoDataSourceInspection - required for "$localize`Delete" statement
            flags.push({
              selected: false,
              id: 'deleteOnlyFromDb',
              message: $localize`Delete
              the service only from database`,
              warningOnSelect: $localize`The service will be deleted, but associated resources will remain and will not be billed anymore`,
            });
            flags.push({
              selected: false,
              id: 'deleteAssociatedResources',
              message: $localize`Delete the service and associated resources`,
              warningOnSelect: $localize`Associated resources and the service will be permanently deleted`,
            });
            importantMessage = $localize`Note that if this service has unsettled usage it will be processed according to client configuration settings.`;
            dialogMessage = $localize`This service has associated resources, please select one of the following options to continue:`;
          } else {
            dialogMessage = $localize`Are you sure?`;
            importantMessage = $localize`This will permanently delete data (including the cost information for the ongoing billing cycle!)`;
          }
          const dialogResult$ = action.notificationService.confirmDialog({
            title: $localize`Delete service ${this.object.id}?`,
            message: dialogMessage,
            importantMessage,
            radioButtons: flags,
          });
          dialogResult$.subscribe((dialogResult: IYesNoDialogResult) => {
            if (dialogResult?.button === 'yes') {
              const deleteOnlyFromDb = !!dialogResult.flags?.deleteOnlyFromDb;
              const deleteAssociatedResources = !!dialogResult.flags?.deleteAssociatedResources;
              this.servicesApiService.delete(this.object.id, {
                delete_only_from_db: deleteOnlyFromDb,
                delete_associated_resources: deleteAssociatedResources,
              }).subscribe({
                next: (response) => {
                  const message = response?.detail || $localize`Service deleted successfully`;
                  action.notificationService.showMessage(message);
                  subscriber.next({success: true, message: null});
                  subscriber.complete();
                },
                error: error => {
                  const message = error?.error?.detail ? error.error.detail : $localize`Failed to delete service`;
                  action.notificationService.showMessage(message);
                  subscriber.next({success: false, message: null});
                  subscriber.complete();
                }
              });
            } else {
              subscriber.next({success: false, message: null});
              subscriber.complete();
            }
          });
        })
      }
    ));

    const alternateEditUrl = this.pluginsComponentsRegistry.getAlternateServiceEditURL(
      this.object.resources?.plugin_name, 'staff', this.object.resources?.service_resources?.id,
    )

    if (alternateEditUrl) {
      actions.splice(0, 0, new RouterLinkAction({
          icon: {name: 'edit', class: 'fl-icons'},
          tooltip: alternateEditUrl.tooltip,
          name: $localize`Edit`,
          routerUrl: alternateEditUrl.url,
          router: this.router,
        }
      ))

      actions.push(new RouterLinkAction({
          icon: {name: 'edit', class: 'fl-icons'},
          tooltip: $localize`Edit service`,
          name: $localize`Edit service`,
          routerUrl: this.config.getPanelUrl(`billing/services/${this.object.id}/edit`),
          router: this.router,
        }
      ));
    } else {
      actions.splice(0, 0, new RouterLinkAction({
          icon: {name: 'edit', class: 'fl-icons'},
          tooltip: $localize`Edit service`,
          name: $localize`Edit`,
          routerUrl: this.config.getPanelUrl(`billing/services/${this.object.id}/edit`),
          router: this.router,
        }
      ));
    }

    return actions;
  }

  getDetailsLink(): string {
    return this.config.getPanelUrl(`billing/services/${this.object.id}`);
  }

  getCardFields(): IDataField[] {
    const datePipe = new DateFormatterPipe(this.config);
    let paid_until = $localize`n/a`;
    let paid_until_title = $localize`n/a`;
    if (this.object.paid_until) {
      try {
        paid_until = datePipe.transform(this.object.paid_until);
        paid_until_title = datePipe.getTitleAttributeValue(this.object.paid_until);
      } catch (e) {
      }
    }

    return [
      {
        name: $localize`Client`,
        value: this.object.client.display,
      },
      {
        name: $localize`Created at`,
        value: `${datePipe.transform(this.object.created_at)}`,
        title: `${datePipe.getTitleAttributeValue(this.object.created_at)}`,
      },
      {
        name: $localize`Paid until`,
        value: paid_until,
        title: paid_until_title,
      }
    ];
  }

  getTabs(): IDetailsTab[] {
    switch (this.state) {
      case 'details':
        const tabs: IDetailsTab[] = [];

        const pluginTabs = this.pluginsComponentsRegistry.getServiceTabs(
          this.object.resources.plugin_name, 'staff',
        )

        let newOverviewTab = false;
        for (const pluginTabName of Object.keys(pluginTabs)) {
          if (pluginTabName.toLowerCase() === 'overview') {
            newOverviewTab = true;
          }
          tabs.push({
            tabName: pluginTabs[pluginTabName].tabDisplayName,
            tabIdentifier: pluginTabName,
            component: pluginTabs[pluginTabName].component,
            data: {
              isPluginTab: true,
            }
          });
        }


        tabs.push(
          {
            tabName: newOverviewTab ? $localize`Service details` : $localize`Overview`,
            tabIdentifier: newOverviewTab ? 'service details' : 'overview',
            component: ServiceDetailsOverviewComponent,
          },
          {
            tabName: $localize`Service cycles`,
            tabIdentifier: 'service-cycles',
            component: ServiceDetailsCyclesComponent,
            featureName: 'billing.service-cycles',
          },
          {
            tabName: $localize`Audit log`,
            tabIdentifier: 'audit-log',
            component: EntityAuditLogComponent,
            featureName: 'utils.activitylog',
            data: {
              entity_type: 'service',
              additional_entity_type: this.pluginsComponentsRegistry.getAdditionalEntityType(
                this.object.resources.plugin_name,
              ),
              additional_entity_id: this.object.resources?.service_resources?.id,
            }
          });

        return tabs;
      case 'edit':
      case 'create':
        return [
          {
            tabName: $localize`Edit`,
            tabIdentifier: 'edit',
            component: ServiceEditFormComponent,
          },
        ];
      case 'change':
        return [
          {
            tabName: $localize`Change`,
            tabIdentifier: 'change',
            component: ServiceChangeFormComponent,
          },
        ];
    }
  }

  getCardTags(): string[] {
    return [];
  }

  getDetailsActions(): IAction[] {
    const actions = [];

    switch (this.state) {
      case 'create':
        actions.push(new RouterLinkAction({
            name: $localize`Cancel`,
            routerUrl: this.config.getPrevUrl(`billing/services`),
            router: this.router,
          }
        ));
        actions.push(new CallbackAction({name: 'Create'}));
        break;
      case 'edit':
        actions.push(new RouterLinkAction({
            name: $localize`Cancel`,
            routerUrl: this.config.getPrevUrl(`billing/services`),
            router: this.router,
          }
        ));
        actions.push(new CallbackAction({name: 'Save'}));
        break;
      default:
        break;
    }

    return actions;
  }
}
