import { Component, Inject, OnInit, Optional } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';
import { CephfsSubvolumeService } from '~/app/shared/api/cephfs-subvolume.service';
import { ConfigurationService } from '~/app/shared/api/configuration.service';
import { ActionLabelsI18n, URLVerbs } from '~/app/shared/constants/app.constants';
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
import { FinishedTask } from '~/app/shared/models/finished-task';
import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
import { Pool } from '../../pool/pool';
import { FormatterService } from '~/app/shared/services/formatter.service';
import { CdTableColumn } from '~/app/shared/models/cd-table-column';
import { CdValidators } from '~/app/shared/forms/cd-validators';
import {
  CephfsSubvolumeInfo,
  SNAPSHOT_VISIBILITY_CONFIG_NAME,
  SNAPSHOT_VISIBILITY_CONFIG_SECTION
} from '~/app/shared/models/cephfs-subvolume.model';
import { ConfigFormModel } from '~/app/shared/components/config-option/config-option.model';
import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
import { OctalToHumanReadablePipe } from '~/app/shared/pipes/octal-to-human-readable.pipe';
import { CdForm } from '~/app/shared/forms/cd-form';
import { CephfsSubvolumeGroupService } from '~/app/shared/api/cephfs-subvolume-group.service';
import { CephfsSubvolumeGroup } from '~/app/shared/models/cephfs-subvolume-group.model';
import { Observable, of } from 'rxjs';
import { catchError, map, switchMap } from 'rxjs/operators';

@Component({
  selector: 'cd-cephfs-subvolume-form',
  templateUrl: './cephfs-subvolume-form.component.html',
  styleUrls: ['./cephfs-subvolume-form.component.scss']
})
export class CephfsSubvolumeFormComponent extends CdForm implements OnInit {
  subvolumeForm: CdFormGroup;

  action: string;
  resource: string;

  subVolumeGroups$: Observable<CephfsSubvolumeGroup[]>;
  subVolumeGroups: CephfsSubvolumeGroup[];
  dataPools: Pool[];

  columns: CdTableColumn[];
  scopePermissions: Array<any> = [];
  initialMode = {
    owner: ['read', 'write', 'execute'],
    group: ['read', 'execute'],
    others: ['read', 'execute']
  };
  scopes: string[] = ['owner', 'group', 'others'];

  showSnapshotVisibility = false;

  constructor(
    private actionLabels: ActionLabelsI18n,
    private taskWrapper: TaskWrapperService,
    private cephFsSubvolumeService: CephfsSubvolumeService,
    private cephFsSubvolumeGroupService: CephfsSubvolumeGroupService,
    private configurationService: ConfigurationService,
    private formatter: FormatterService,
    private dimlessBinary: DimlessBinaryPipe,
    private octalToHumanReadable: OctalToHumanReadablePipe,

    @Optional() @Inject('fsName') public fsName: string,
    @Optional() @Inject('subVolumeName') public subVolumeName: string,
    @Optional() @Inject('subVolumeGroupName') public subVolumeGroupName: string,
    @Optional() @Inject('pools') public pools: Pool[],
    @Optional() @Inject('isEdit') public isEdit = false
  ) {
    super();
    this.resource = $localize`Subvolume`;
  }

  ngOnInit(): void {
    this.action = this.actionLabels.CREATE;
    this.columns = [
      {
        prop: 'scope',
        name: $localize`All`,
        flexGrow: 0.5
      },
      {
        prop: 'read',
        name: $localize`Read`,
        flexGrow: 0.5,
        cellClass: 'text-center'
      },
      {
        prop: 'write',
        name: $localize`Write`,
        flexGrow: 0.5,
        cellClass: 'text-center'
      },
      {
        prop: 'execute',
        name: $localize`Execute`,
        flexGrow: 0.5,
        cellClass: 'text-center'
      }
    ];

    this.subVolumeGroups$ = this.cephFsSubvolumeGroupService.get(this.fsName);
    this.dataPools = this.pools.filter((pool) => pool.type === 'data');
    this.createForm();

    this.loadSnapshotVisibilityConfig().subscribe(() => {
      this.isEdit ? this.populateForm() : this.loadingReady();
    });
  }

  private loadSnapshotVisibilityConfig(): Observable<void> {
    return this.configurationService.filter([SNAPSHOT_VISIBILITY_CONFIG_NAME]).pipe(
      map((configOptions: ConfigFormModel[]) => {
        const options = configOptions ?? [];
        const option = options.find((opt) => opt.name === SNAPSHOT_VISIBILITY_CONFIG_NAME);
        this.showSnapshotVisibility = this.isSnapshotVisibilityEnabled(option);
        const visibilityControl = this.subvolumeForm?.get('snapshotVisibility');
        if (!this.showSnapshotVisibility && visibilityControl) {
          visibilityControl.disable();
          visibilityControl.setValue(false);
        }
      }),
      catchError(() => {
        this.showSnapshotVisibility = false;
        return of(undefined);
      })
    );
  }

  private isSnapshotVisibilityEnabled(option?: ConfigFormModel) {
    const values = option?.value ?? [];
    const clientValue = values.find((entry) => entry.section === SNAPSHOT_VISIBILITY_CONFIG_SECTION)
      ?.value;
    return String(clientValue).toLowerCase() === 'true';
  }

  createForm() {
    this.subvolumeForm = new CdFormGroup({
      volumeName: new FormControl({ value: this.fsName, disabled: true }),
      subvolumeName: new FormControl('', {
        validators: [Validators.required, Validators.pattern(/^[.A-Za-z0-9_-]+$/)],
        asyncValidators: [
          CdValidators.unique(
            this.cephFsSubvolumeService.exists,
            this.cephFsSubvolumeService,
            null,
            null,
            this.fsName,
            this.subVolumeGroupName
          )
        ]
      }),
      subvolumeGroupName: new FormControl(this.subVolumeGroupName),
      pool: new FormControl(this.dataPools[0]?.pool, {
        validators: [Validators.required]
      }),
      size: new FormControl(null, {
        updateOn: 'blur'
      }),
      uid: new FormControl(null),
      gid: new FormControl(null),
      mode: new FormControl({}),
      isolatedNamespace: new FormControl(false),
      snapshotVisibility: new FormControl(true)
    });
  }

  populateForm() {
    this.action = this.actionLabels.EDIT;
    this.cephFsSubvolumeService
      .info(this.fsName, this.subVolumeName, this.subVolumeGroupName)
      .subscribe((resp: CephfsSubvolumeInfo) => {
        // Disabled these fields since its not editable
        this.subvolumeForm.get('subvolumeName').disable();
        this.subvolumeForm.get('subvolumeGroupName').disable();
        this.subvolumeForm.get('pool').disable();
        this.subvolumeForm.get('uid').disable();
        this.subvolumeForm.get('gid').disable();

        this.subvolumeForm.get('isolatedNamespace').disable();
        this.subvolumeForm.get('subvolumeName').setValue(this.subVolumeName);
        this.subvolumeForm.get('subvolumeGroupName').setValue(this.subVolumeGroupName);
        if (resp.bytes_quota !== 'infinite') {
          this.subvolumeForm.get('size').setValue(this.dimlessBinary.transform(resp.bytes_quota));
        }
        this.subvolumeForm.get('uid').setValue(resp.uid);
        this.subvolumeForm.get('gid').setValue(resp.gid);
        this.subvolumeForm.get('isolatedNamespace').setValue(resp.pool_namespace);
        this.initialMode = this.octalToHumanReadable.transform(resp.mode, true);

        if (!this.showSnapshotVisibility) {
          this.subvolumeForm.get('snapshotVisibility').setValue(false);
        }
        this.loadSnapshotVisibility();
      });
  }

  private loadSnapshotVisibility() {
    this.cephFsSubvolumeService
      .getSnapshotVisibility(this.fsName, this.subVolumeName, this.subVolumeGroupName)
      .subscribe({
        next: (visibilityResp) => {
          const visibility = String(visibilityResp) === '1';
          this.subvolumeForm.get('snapshotVisibility').setValue(visibility);
          this.loadingReady();
        },
        error: () => {
          this.subvolumeForm.get('snapshotVisibility').setValue(true);
          this.loadingReady();
        }
      });
  }

  private setSnapshotVisibility(
    subVolumeName: string,
    subVolumeGroupName: string,
    visible: boolean
  ) {
    return this.showSnapshotVisibility
      ? this.cephFsSubvolumeService.setSnapshotVisibility(
          this.fsName,
          subVolumeName,
          visible,
          subVolumeGroupName
        )
      : of({ status: 200 });
  }

  submit() {
    const subVolumeName = this.subvolumeForm.getValue('subvolumeName');
    const subVolumeGroupName = this.subvolumeForm.getValue('subvolumeGroupName');
    const pool = this.subvolumeForm.getValue('pool');
    const size = this.formatter.toBytes(this.subvolumeForm.getValue('size')) || 0;
    const uid = this.subvolumeForm.getValue('uid');
    const gid = this.subvolumeForm.getValue('gid');
    const mode = this.formatter.toOctalPermission(this.subvolumeForm.getValue('mode'));
    const isolatedNamespace = this.subvolumeForm.getValue('isolatedNamespace');
    const snapshotVisibility = this.subvolumeForm.getValue('snapshotVisibility');

    if (this.isEdit) {
      const editSize = size === 0 ? 'infinite' : size;
      const visibilityCall = this.setSnapshotVisibility(
        subVolumeName,
        subVolumeGroupName,
        snapshotVisibility
      );

      this.taskWrapper
        .wrapTaskAroundCall({
          task: new FinishedTask('cephfs/subvolume/' + URLVerbs.EDIT, {
            subVolumeName: subVolumeName
          }),
          call: this.cephFsSubvolumeService
            .update(this.fsName, subVolumeName, String(editSize), subVolumeGroupName)
            .pipe(switchMap(() => visibilityCall))
        })
        .subscribe({
          error: () => {
            this.subvolumeForm.setErrors({ cdSubmitButton: true });
          },
          complete: () => {
            this.closeModal();
          }
        });
    } else {
      this.taskWrapper
        .wrapTaskAroundCall({
          task: new FinishedTask('cephfs/subvolume/' + URLVerbs.CREATE, {
            subVolumeName: subVolumeName
          }),
          call: this.cephFsSubvolumeService
            .create(
              this.fsName,
              subVolumeName,
              subVolumeGroupName,
              pool,
              String(size),
              uid,
              gid,
              mode,
              isolatedNamespace
            )
            .pipe(
              switchMap(() =>
                this.setSnapshotVisibility(subVolumeName, subVolumeGroupName, snapshotVisibility)
              )
            )
        })
        .subscribe({
          error: () => {
            this.subvolumeForm.setErrors({ cdSubmitButton: true });
          },
          complete: () => {
            this.closeModal();
          }
        });
    }
  }
}
