import { ChangeDetectorRef, Component, NgZone, OnInit } from '@angular/core';
import { DetailsFormBase } from '@objects-view/details-form-base';
import { IInvoiceModel } from '@fleio-api/billing/model/invoice.model';
import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { IClientModel } from '@fleio-api/client-user/model/client.model';
import { Observable, of } from 'rxjs';
import { ClientsApiService } from '@fleio-api/client-user/client/clients-api.service';
import { ActivatedRoute, Router } from '@angular/router';
import { IInvoiceCreateOptionsModel } from '@fleio-api/billing/model/invoice-create-options.model';
import { IActionResult } from '@objects-view/interfaces/actions/action-result';
import { InvoicesApiService } from '@fleio-api/billing/invoices/invoices-api.service';
import { ConfigService } from '@shared/config/config.service';
import { IInvoiceItemModel } from '@fleio-api/billing/model/invoice-item.model';
import { ServicesApiService } from '@fleio-api/billing/services/service-api.service';
import { ServiceCyclesApiService } from '@fleio-api/billing/service-cycles/service-cycles-api.service';
import { IServiceCycleModel } from '@fleio-api/billing/service-cycles/models/service-cycle-model';
import { ValidatorsHelper } from '@shared/ui-api/helpers/validators-helper';

@Component({
  selector: 'app-invoice-edit-form',
  templateUrl: './invoice-edit-form.component.html',
  styleUrls: ['./invoice-edit-form.component.scss']
})
export class InvoiceEditFormComponent extends DetailsFormBase<IInvoiceModel> implements OnInit {
  invoiceForm = this.formBuilder.group({
    client: ['', Validators.required],
    status: ['unpaid', Validators.required],
    issue_date: [Date.now(), Validators.required],
    due_date: [Date.now(), Validators.required],
    items: this.formBuilder.array([]),
  });

  nonSelectableItemTypes = ['serviceUpgrade', 'validation', 'validation_cr']

  client = this.invoiceForm.controls.client;
  createOptions: IInvoiceCreateOptionsModel;
  addInvoiceItemTooltip = $localize`Add invoice item`;
  serviceCycles: WeakMap<UntypedFormGroup, IServiceCycleModel[]> = new WeakMap<UntypedFormGroup, []>();

  constructor(
    private formBuilder: UntypedFormBuilder,
    private clientsApi: ClientsApiService,
    private activatedRoute: ActivatedRoute,
    private invoicesApi: InvoicesApiService,
    private router: Router,
    public config: ConfigService,
    private servicesApi: ServicesApiService,
    private serviceCyclesApi: ServiceCyclesApiService,
    ngZone: NgZone,
    changeDetectorRef: ChangeDetectorRef,
  ) {
    super(ngZone, changeDetectorRef, config);
  }

  ngOnInit() {
    super.ngOnInit();
    if (this.objectController) {
      this.objectController.actionCallback = () => this.saveInvoice();
    }
    this.createOptions = this.activatedRoute.snapshot.data.createOptions;
    if (this.object) {
      if (this.object.id) {
        this.invoiceForm.controls.client.disable();
      }
      if (this.object.is_fiscal) {
        this.invoiceForm.addControl(
          'fiscal_date', this.formBuilder.control(this.object.fiscal_date, Validators.required)
        );
        this.invoiceForm.addControl(
          'fiscal_due_date', this.formBuilder.control(this.object.fiscal_due_date, Validators.required)
        );
      }
      if (Object.keys(this.object).length > 0) {
        this.invoiceForm.patchValue(this.object);
        if (this.object.items) {
          this.object.items.map(
            item => (this.invoiceForm.controls.items as UntypedFormArray).push(this.initInvoiceItem(item))
          );
        }
      } else {
        this.invoiceForm.patchValue({
          issue_date: new Date(),
          due_date: new Date(),
        });

        const queryParams = this.activatedRoute.snapshot.queryParams;
        if (queryParams.client_id) {
          this.clientsApi.get(queryParams.client_id).subscribe(client => {
            this.client.setValue(client);
          });
        }
      }
    }

    if (this.object && !this.object.id) {
      this.client.valueChanges.subscribe(client => {
        if (client && client.id) {
          this.servicesApi.list({
            client: client.id,
          }).subscribe(services => {
            this.createOptions.services = services.objects;
          });
        }
      });
    }
  }

  initInvoiceItem(item: IInvoiceItemModel = null) {
    const invoiceItemGroup = this.formBuilder.group({
      description: [item ? item.description : '', Validators.required],
      amount: [item ? item.amount : '', ValidatorsHelper.requiredNumericValueValidator],
      item_type: [item ? item.item_type : 'other', Validators.required],
      taxed: [item ? item.taxed : true],
    });

    if (item && this.nonSelectableItemTypes.includes(item.item_type)) {
      invoiceItemGroup.controls.item_type.disable();
    }

    const hasServiceFields = item?.item_type === 'service';
    const isUpgrade =  !!(item?.item_type === 'serviceUpgrade' && item?.service);
    if (hasServiceFields || isUpgrade) {
      if (hasServiceFields) {
        invoiceItemGroup.addControl('service_cycle', this.formBuilder.control(item.service_cycle, Validators.required));
      } else {
        invoiceItemGroup.addControl('service_cycle', this.formBuilder.control(item.service_cycle));
      }
      invoiceItemGroup.addControl('service', this.formBuilder.control(null, Validators.required));
      invoiceItemGroup.controls.service.valueChanges.subscribe(serviceId => {
        if (item.service_cycle) {
          this.serviceCyclesApi.get(item.service_cycle).subscribe(currentServiceCycle => {
            this.serviceCyclesApi.list({
              filtering: `service:${serviceId}+invoice_items__isnull:True`,
            }).subscribe(cycleList => {
              const availableServiceCycles = cycleList.objects;
              if (item?.service === serviceId) {
                availableServiceCycles.push(currentServiceCycle);
              }
              this.serviceCycles.set(invoiceItemGroup, availableServiceCycles);
              if (serviceId === item?.service) {
                invoiceItemGroup.controls.service_cycle.setValue(item.service_cycle);
              } else {
                invoiceItemGroup.controls.service_cycle.setValue(null);
              }
            });
          });
        } else {
          this.serviceCyclesApi.list({
            filtering: `service:${serviceId}+invoice_items__isnull:True`,
          }).subscribe(cycleList => {
            const availableServiceCycles = cycleList.objects;
            this.serviceCycles.set(invoiceItemGroup, availableServiceCycles);
            invoiceItemGroup.controls.service_cycle.setValue(null);
          });
        }
      });
      invoiceItemGroup.controls.service.setValue(item.service);
    }

    invoiceItemGroup.controls.item_type.valueChanges.subscribe(item_type => {
      if (item_type === 'service') {
        invoiceItemGroup.addControl('service', this.formBuilder.control(null, Validators.required));
        invoiceItemGroup.addControl('service_cycle', this.formBuilder.control(null, Validators.required));
        invoiceItemGroup.controls.service.valueChanges.subscribe(serviceId => {
          this.serviceCyclesApi.list({
            filtering: `service:${serviceId}+invoice_items__isnull:True`,
          }).subscribe(cycleList => {
            this.serviceCycles.set(invoiceItemGroup, cycleList.objects);
            if (serviceId === item?.service) {
              invoiceItemGroup.controls.service_cycle.setValue(item.service_cycle);
            } else {
              invoiceItemGroup.controls.service_cycle.setValue(null);
            }
          });
        });
      } else {
        invoiceItemGroup.removeControl('service_cycle');
        invoiceItemGroup.removeControl('service');
      }
    })

    if (item?.id) {
      invoiceItemGroup.addControl('id', this.formBuilder.control(item.id));
    }

    return invoiceItemGroup;
  }

  addInvoiceItem(event) {
    event.preventDefault();
    const items = this.invoiceForm.controls.items as UntypedFormArray;
    items.push(this.initInvoiceItem());
    this.changeDetectorRef.detectChanges();
    const elements = document.getElementsByTagName('textarea') as HTMLCollection;
    if (elements && elements.length) {
      const lastElement = elements[elements.length - 1];
      lastElement.scrollIntoView({behavior: 'smooth'});
    }
  }

  removeItem(index) {
    const items = this.invoiceForm.controls.items as UntypedFormArray;
    items.removeAt(index);
  }

  private saveInvoice(): Observable<IActionResult> {
    const value = this.invoiceForm.getRawValue() as IInvoiceModel;
    if (value.client && value.client.id) {
      value.client = (value.client as IClientModel).id;
    }

    this.createOrUpdate(this.invoicesApi, value).subscribe(() => {
      this.router.navigateByUrl(
        this.config.getPrevUrl('billing/invoices')
      ).catch(() => {
      });
    });

    return of(null);
  }
}
