import { AsyncPipe, NgTemplateOutlet } from '@angular/common'
import {
  Component,
  forwardRef,
  inject,
  Input,
  OnDestroy,
  OnInit,
  signal,
} from '@angular/core'
import {
  FormsModule,
  NG_VALUE_ACCESSOR,
  ReactiveFormsModule,
} from '@angular/forms'
import { RouterModule } from '@angular/router'
import { NgSelectModule } from '@ng-select/ng-select'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import {
  catchError,
  concat,
  distinctUntilChanged,
  map,
  Observable,
  of,
  Subject,
  switchMap,
  takeUntil,
  tap,
} from 'rxjs'
import { Document } from 'src/app/data/document'
import { FILTER_SIMPLE_TITLE } from 'src/app/data/filter-rule-type'
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
import { DocumentService } from 'src/app/services/rest/document.service'
import { AbstractInputComponent } from '../abstract-input'

@Component({
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => DocumentLinkComponent),
      multi: true,
    },
  ],
  selector: 'pngx-input-document-link',
  templateUrl: './document-link.component.html',
  styleUrls: ['./document-link.component.scss'],
  imports: [
    CustomDatePipe,
    AsyncPipe,
    FormsModule,
    ReactiveFormsModule,
    RouterModule,
    NgTemplateOutlet,
    NgSelectModule,
    NgxBootstrapIconsModule,
  ],
})
export class DocumentLinkComponent
  extends AbstractInputComponent<any[]>
  implements OnInit, OnDestroy
{
  private documentsService = inject(DocumentService)

  documentsInput$ = new Subject<string>()
  foundDocuments$: Observable<Document[]>
  readonly loading = signal(false)
  selectedDocuments: Document[] = []

  private unsubscribeNotifier: Subject<any> = new Subject()

  @Input()
  notFoundText: string = $localize`No documents found`

  @Input()
  parentDocumentID: number

  @Input()
  minimal: boolean = false

  @Input()
  placeholder: string = $localize`Search for documents`

  /**
   * Parent for ng-select dropdown, needed to prevent close on click.
   */
  @Input()
  appendTo: string = null

  get selectedDocumentIDs(): number[] {
    return this.selectedDocuments.map((d) => d.id)
  }

  ngOnInit() {
    this.loadDocs()
  }

  writeValue(documentIDs: number[]): void {
    if (!documentIDs || documentIDs.length === 0) {
      this.selectedDocuments = []
      super.writeValue([])
    } else {
      this.loading.set(true)
      this.documentsService
        .getFew(documentIDs, { fields: 'id,title' })
        .pipe(takeUntil(this.unsubscribeNotifier))
        .subscribe((documentResults) => {
          this.loading.set(false)
          this.selectedDocuments = documentIDs.map(
            (id) => documentResults.results.find((d) => d.id === id) ?? { id }
          )
          super.writeValue(documentIDs)
        })
    }
  }

  private loadDocs() {
    this.foundDocuments$ = concat(
      of([]), // default items
      this.documentsInput$.pipe(
        distinctUntilChanged(),
        takeUntil(this.unsubscribeNotifier),
        tap(() => this.loading.set(true)),
        switchMap((title) =>
          this.documentsService
            .listFiltered(
              1,
              null,
              'created',
              true,
              [{ rule_type: FILTER_SIMPLE_TITLE, value: title }],
              { truncate_content: true }
            )
            .pipe(
              map((results) =>
                results.results.filter(
                  (d) =>
                    d.id !== this.parentDocumentID &&
                    !this.selectedDocuments.find((sd) => sd.id === d.id)
                )
              ),
              catchError(() => of([])), // empty on error
              tap(() => this.loading.set(false))
            )
        )
      )
    )
  }

  unselect(document: Document): void {
    if (this.disabled) return

    this.selectedDocuments = this.selectedDocuments.filter(
      (d) => d && d.id !== document.id
    )
    this.onChange(this.selectedDocuments.map((d) => d.id))
  }

  compareDocuments(document: Document, selectedDocument: Document) {
    return document.id === selectedDocument.id
  }

  trackByFn(item: Document) {
    return item.id
  }

  ngOnDestroy(): void {
    this.unsubscribeNotifier.next(true)
    this.unsubscribeNotifier.complete()
  }
}
