import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Override } from 'ng-stalk/core/model';
import {
  CrudAction,
  ActionButton,
  ActionButtonPreset,
  CrudModal2Component,
} from 'ng-stalk/core/crud';
import {
  MagCodeCacheService,
  MagDateService,
} from 'ng-stalk/core/service';
import { CodeAndName} from 'ng-stalk/core/types';
import { MagUtil } from 'ng-stalk/core/util';
import { NzModalRef } from 'ng-zorro-antd/modal';
import { BehaviorSubject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';

import { DefaultApiErrorHandler } from '../../../service/default-api-error-handler';
import { Modify<%= classify(name) %>Request } from '../service/<%= dasherize(name) %>-service/modify-<%= dasherize(name) %>-request';
import { Create<%= classify(name) %>Request } from '../service/<%= dasherize(name) %>-service/create-<%= dasherize(name) %>-request';
import { <%= classify(name) %>FormValidator } from './<%= dasherize(name) %>-form-validator';
import { <%= classify(name) %>Service } from '../service/<%= dasherize(name) %>-service/<%= dasherize(name) %>.service';
import { <%= classify(name) %>FormModel } from './<%= dasherize(name) %>-form-model';
import { <%= classify(name) %>Dto } from '../sample/<%= dasherize(name) %>-dto';

@Component({
  selector: 'app-<%= dasherize(name) %>-edit-modal',
  templateUrl: './<%= dasherize(name) %>-edit-modal.component.html',
  styleUrls: ['./<%= dasherize(name) %>-edit-modal.component.less']
})
export class <%= classify(name) %>EditModalComponent extends CrudModal2Component<<%= classify(name) %>Dto> implements OnInit, OnDestroy {

  // --------------------------------------------------------------------------
  // MAG: CRUD 모달 공통 속성
  // --------------------------------------------------------------------------

  mode: string; // add | edit
  model?: <%= classify(name) %>Dto;
  modelForm: FormGroup;
  actionButtons: BehaviorSubject<ActionButton[]>;

  // TODO share url 이 있을 경우 공유 링크가 모달 상단에 표시된다. 필요 없으면 null 처리하자
  shareUrl: string;
  // shareUrl = 'http://daum.net';

  // --------------------------------------------------------------------------
  // 어플리케이션 속성
  // --------------------------------------------------------------------------

  // TODO 어플리케이션에 필요한 커스텀 프로퍼티는 여기 하위로 지정하자.

  langs: BehaviorSubject<string[]>;

  // TODO 샘플 코드에서는 가이드를 위해서 폼 입력에 필요한 배열을 하드코딩하여 기록한다. magCodeCacheService 등을 통해서 적절하게 변경을 해야한다.
  languages: CodeAndName[] = [
    {code: 'en', name: '영어'},
    {code: 'ko', name: '한국어'}
  ];
  statuses: CodeAndName[] = [
    {code: 'TEMP', name: '임시'},
    {code: 'OK', name: '정상'},
    {code: 'EXPIRED', name: '만료'}
  ];
  categories: CodeAndName[] = [
    {code: 'TEMP', name: '임시'},
    {code: 'OK', name: '정상'},
    {code: 'EXPIRED', name: '만료'}
  ];

  constructor(
    public dialogRef: NzModalRef<<%= classify(name) %>EditModalComponent>,
    private formBuilder: FormBuilder,
    private translateService: TranslateService,
    private magCodeCacheService: MagCodeCacheService,
    private magDateService: MagDateService,
    private <%= name %>Service: <%= classify(name) %>Service,
  ) {
    super(dialogRef);
    this.actionButtons = new BehaviorSubject<ActionButton[]>([]);
    this.langs = new BehaviorSubject<string[]>([]);
  }

  // --------------------------------------------------------------------------
  // MAG: 라이프 사이클 및 필수 기능 구현(oInit, makeDialogTitle, onAction, onDestroy)
  // --------------------------------------------------------------------------

  @Override
  ngOnInit(): void {
    // 1. 폼의  기본 값 및 mode 를 설정한다.
    let formModel: <%= classify(name) %>FormModel;
    if (this.data.action === CrudAction.openAddModal) {
      this.mode = 'add';
      formModel = {
        languages: ['ko', 'en'],
        i18nNames: [],
        type: 'ETC',
        status: 'TEMP',
        requiredPassword: false,
        unlimitedExpiration: true,
        accessBetweenAt: [],
      };
      this.actionButtons.next(ActionButtonPreset.add());
    } else {
      this.mode = 'edit';
      this.actionButtons.next(ActionButtonPreset.edit());
      this.model = this.data.model;

      // 도메인 모델로부터 폼 모델로 전환한다.

      formModel = {
       ...this.data.model,
      };

      // 동일하지 않은 프로퍼티는 여기서뷰터 수동 매핑해준다.
      // formModel.loginId = data.model.loginId;
    }

    // 2. 기본 값이 채워진 폼 모델로부터 폼 그룹을 생성한다.
    this.langs.next(formModel.languages);
    this.modelForm = this.createForm(formModel);

    // 3. (Optional) 폼 그룹/컨트톨의 동적 변경이 필요한 경우 설정한다.
    this.attachEventsToForm();

    super.ngOnInit();
  }

  createForm(model: <%= classify(name) %>FormModel): FormGroup {
    return this.formBuilder.group({
      name: [model.name, [Validators.required]],
      languages: [model.languages, [Validators.required]],
      i18nNames: [model.i18nNames, [Validators.required]],
      type: [model.type, [Validators.required]],
      status: [model.status, [Validators.required]],
      categoryIds: [model.categoryIds, []],
      requiredPassword: [model.requiredPassword, [Validators.required]],
      lockPassword: [{
        value: model.lockPassword,
        disabled: model.requiredPassword !== true
      }, model.lockPassword ? [Validators.required] : []],
      expiredAt: [model.expiredAt, []],
      unlimitedExpiration: [model.unlimitedExpiration, [Validators.required]],
      accessBetweenAt: [model.accessBetweenAt, []],
      memo: [model.memo, []],
    }, {
      validators: [<%= classify(name) %>FormValidator.valid()]
    });
  }

  /**
   * modelForm 생성 이후에 폼 그룹/컨트톨의 동적 변경이 필요한 경우 설정한다.
   */
  private attachEventsToForm(): void {
    // TODO 본 메서드 내용이 필요없으면 다 삭제해도 된다.

    // 본 예시는 '다국어 설정' 필드에서 언어가 체크될 때마다, '언어별 이름' 입력 값에 해당 언어가 설정되도록 `langs` subject 에 이벤트를 발행한다.
    this.modelForm.controls['languages'].valueChanges
      .pipe(takeUntil(this.unsubscribeAll))
      .subscribe((changeValue: any) => {
        this.langs.next(changeValue);
      });

    // 본 예시는 'requiredPassword' 스위치의 토글 여부에 따라 '잠금 비밀번호' 필드를 활성/비활성한다.
    this.modelForm.controls['requiredPassword'].valueChanges
      .pipe(takeUntil(this.unsubscribeAll))
      .subscribe((changeValue: boolean) => {
        if (changeValue === true) {
          MagUtil.enableFields(this.modelForm, ['lockPassword']);
        } else {
          MagUtil.disableFields(this.modelForm, ['lockPassword']);
        }
      });

    // 본 예시는 '무제한' 체크박스가 설정될 경우, '만료일시' 필드를 비활성화하고 해당 필드 값을 null 로 업데이트한다.
    this.modelForm.controls['unlimitedExpiration'].valueChanges
      .pipe(takeUntil(this.unsubscribeAll))
      .subscribe((changeValue: boolean) => {
        const value: <%= classify(name) %>FormModel = this.modelForm.getRawValue();

        if (changeValue === true) {
          this.modelForm.patchValue({
            expiredAt: null,
          });
          MagUtil.disableFields(this.modelForm, ['expiredAt']);
        } else {
          MagUtil.enableFields(this.modelForm, ['expiredAt']);
        }
      });
  }

  @Override
  makeDialogTitle(): string {
    if (this.mode === 'add') {
      return '<%= displayName %> 추가하기';
    } else if (this.mode === 'edit') {
      return '<%= displayName %> 수정하기';
    }
  }

  @Override
  onAction(action: string | CrudAction): void {
    if (action === CrudAction.doAdd) {
      this.add();
    } else if (action === CrudAction.doEdit) {
      this.modify();
    } else {
      throw new Error('not implemented action: ' + action);
    }
  }

  @Override
  ngOnDestroy(): void {
    super.ngOnDestroy();
  }

  isRequired(formControlName: string): boolean {
    if (!this.modelForm.controls[formControlName]) {
      console.error('not found control: ', formControlName);
    }
    return MagUtil.hasRequiredField(this.modelForm.controls[formControlName]);
  }

  // --------------------------------------------------------------------------
  // 어플리케이션 비즈니스 로직
  // --------------------------------------------------------------------------

  add(): void {
    const formModel: <%= classify(name) %>FormModel = this.modelForm.getRawValue();

    // 폼에서 추출한 값으로 API 요청 정보를 만든다.
    const request: Create<%= classify(name) %>Request = {
      ...formModel,
      expiredAt: this.magDateService.dateToIso8601(formModel.expiredAt), // date -> string
      accessStartAt: this.magDateService.dateToIso8601(formModel.accessBetweenAt[0]),
      accessEndAt: this.magDateService.dateToIso8601(formModel.accessBetweenAt[1]),
    };
    this.<%= name %>Service.create(request).then((updatedModel: <%= classify(name) %>Dto) => {
      this.doModalCompletedAction(updatedModel);
    }).catch((errorHandler: DefaultApiErrorHandler) => {
      errorHandler.handle();
    });
  }

  modify(): void {
    const formModel: <%= classify(name) %>FormModel = this.modelForm.getRawValue();

    // 폼에서 추출한 값으로 API 요청 정보를 만든다.
    const request: Modify<%= classify(name) %>Request = {
      ...formModel,
      expiredAt: this.magDateService.dateToIso8601(formModel.expiredAt), // date -> string
      accessStartAt: this.magDateService.dateToIso8601(formModel.accessBetweenAt[0]),
      accessEndAt: this.magDateService.dateToIso8601(formModel.accessBetweenAt[1]),
    };
    this.<%= name %>Service.modify(this.model.id, request).then((updatedModel: <%= classify(name) %>Dto) => {
      this.doModalCompletedAction(updatedModel);
    }).catch((errorHandler: DefaultApiErrorHandler) => {
      errorHandler.handle();
    });
  }

}
