import { DragDropModule } from '@angular/cdk/drag-drop'
import { DatePipe } from '@angular/common'
import {
  HttpErrorResponse,
  provideHttpClient,
  withInterceptorsFromDi,
} from '@angular/common/http'
import { provideHttpClientTesting } from '@angular/common/http/testing'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { By } from '@angular/platform-browser'
import { Router } from '@angular/router'
import { RouterTestingModule } from '@angular/router/testing'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
import { Subject, of, throwError } from 'rxjs'
import { routes } from 'src/app/app-routing.module'
import { CustomFieldDisplayComponent } from 'src/app/components/common/custom-field-display/custom-field-display.component'
import { PreviewPopupComponent } from 'src/app/components/common/preview-popup/preview-popup.component'
import { CustomFieldDataType } from 'src/app/data/custom-field'
import { DisplayField, DisplayMode } from 'src/app/data/document'
import {
  FILTER_CORRESPONDENT,
  FILTER_DOCUMENT_TYPE,
  FILTER_FULLTEXT_MORELIKE,
  FILTER_HAS_TAGS_ALL,
  FILTER_OWNER_ANY,
  FILTER_STORAGE_PATH,
} from 'src/app/data/filter-rule-type'
import { SavedView } from 'src/app/data/saved-view'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
import { SafeUrlPipe } from 'src/app/pipes/safeurl.pipe'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { PermissionsService } from 'src/app/services/permissions.service'
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
import { DocumentService } from 'src/app/services/rest/document.service'
import {
  FileStatus,
  WebsocketStatusService,
} from 'src/app/services/websocket-status.service'
import { WidgetFrameComponent } from '../widget-frame/widget-frame.component'
import { SavedViewWidgetComponent } from './saved-view-widget.component'

const savedView: SavedView = {
  id: 1,
  name: 'Saved View 1',
  sort_field: 'added',
  sort_reverse: true,
  show_in_sidebar: true,
  show_on_dashboard: true,
  filter_rules: [
    {
      rule_type: FILTER_HAS_TAGS_ALL,
      value: '1,2',
    },
  ],
  page_size: 20,
  display_mode: DisplayMode.TABLE,
  display_fields: [
    DisplayField.CREATED,
    DisplayField.TITLE,
    DisplayField.TAGS,
    DisplayField.CORRESPONDENT,
    DisplayField.DOCUMENT_TYPE,
    DisplayField.STORAGE_PATH,
    DisplayField.PAGE_COUNT,
    `${DisplayField.CUSTOM_FIELD}11` as any,
    `${DisplayField.CUSTOM_FIELD}15` as any,
  ],
}

const documentResults = [
  {
    id: 2,
    title: 'doc2',
    custom_fields: [
      { id: 1, field: 11, created: new Date(), value: 'custom', document: 2 },
    ],
  },
  {
    id: 3,
    title: 'doc3',
    correspondent: 0,
    custom_fields: [],
  },
  {
    id: 4,
    title: 'doc4',
    custom_fields: [
      { id: 32, field: 3, created: new Date(), value: 'EUR123', document: 4 },
    ],
  },
  {
    id: 5,
    title: 'doc5',
    custom_fields: [
      {
        id: 22,
        field: 15,
        created: new Date(),
        value: [123, 456, 789],
        document: 5,
      },
    ],
  },
]

describe('SavedViewWidgetComponent', () => {
  let component: SavedViewWidgetComponent
  let fixture: ComponentFixture<SavedViewWidgetComponent>
  let documentService: DocumentService
  let websocketStatusService: WebsocketStatusService
  let documentListViewService: DocumentListViewService
  let router: Router

  beforeEach(async () => {
    TestBed.configureTestingModule({
      imports: [
        NgbModule,
        RouterTestingModule.withRoutes(routes),
        DragDropModule,
        NgxBootstrapIconsModule.pick(allIcons),
        SavedViewWidgetComponent,
        WidgetFrameComponent,
        IfPermissionsDirective,
        CustomDatePipe,
        DocumentTitlePipe,
        SafeUrlPipe,
        PreviewPopupComponent,
        CustomFieldDisplayComponent,
      ],
      providers: [
        PermissionsGuard,
        DocumentService,
        {
          provide: PermissionsService,
          useValue: {
            currentUserCan: () => true,
          },
        },
        CustomDatePipe,
        DatePipe,
        {
          provide: CustomFieldsService,
          useValue: {
            listAll: () =>
              of({
                all: [3, 11, 15],
                count: 3,
                results: [
                  {
                    id: 3,
                    name: 'Custom field 3',
                    data_type: CustomFieldDataType.Monetary,
                  },
                  {
                    id: 11,
                    name: 'Custom Field 11',
                    data_type: CustomFieldDataType.String,
                  },
                  {
                    id: 15,
                    name: 'Custom Field 15',
                    data_type: CustomFieldDataType.DocumentLink,
                  },
                ],
              }),
          },
        },
        provideHttpClient(withInterceptorsFromDi()),
        provideHttpClientTesting(),
      ],
    }).compileComponents()

    documentService = TestBed.inject(DocumentService)
    websocketStatusService = TestBed.inject(WebsocketStatusService)
    documentListViewService = TestBed.inject(DocumentListViewService)
    router = TestBed.inject(Router)
    fixture = TestBed.createComponent(SavedViewWidgetComponent)
    component = fixture.componentInstance
    component.savedView = savedView

    fixture.detectChanges()
  })

  it('should show a list of documents', async () => {
    jest.useFakeTimers()
    jest.spyOn(documentService, 'listFiltered').mockReturnValue(
      of({
        all: [2, 3],
        count: 2,
        results: documentResults,
      })
    )
    component.ngOnInit()
    jest.advanceTimersByTime(500)
    await fixture.whenStable()
    fixture.detectChanges()
    expect(fixture.debugElement.nativeElement.textContent).toContain('doc2')
    expect(fixture.debugElement.nativeElement.textContent).toContain('doc3')
    // preview + download buttons
    expect(
      fixture.debugElement.queryAll(By.css('td a.btn'))[0].attributes['href']
    ).toEqual(documentService.getPreviewUrl(documentResults[0].id))
    expect(
      fixture.debugElement.queryAll(By.css('td a.btn'))[1].attributes['href']
    ).toEqual(component.getDownloadUrl(documentResults[0]))
    jest.useRealTimers()
  })

  it('should call api endpoint and load results', () => {
    const listAllSpy = jest.spyOn(documentService, 'listFiltered')
    listAllSpy.mockReturnValue(
      of({
        all: [2, 3],
        count: 2,
        results: documentResults,
      })
    )
    component.ngOnInit()
    expect(listAllSpy).toHaveBeenCalledWith(
      1,
      20,
      savedView.sort_field,
      savedView.sort_reverse,
      savedView.filter_rules,
      {
        truncate_content: true,
      }
    )
    fixture.detectChanges()
    expect(component.documents()).toEqual(documentResults)
  })

  it('should show an error if documents fail to load', () => {
    jest.spyOn(documentService, 'listFiltered').mockReturnValue(
      throwError(
        () =>
          new HttpErrorResponse({
            error: { added__date__lte: ['Enter a valid date.'] },
            status: 400,
          })
      )
    )

    component.reload()
    fixture.detectChanges()

    expect(component.loading()).toBe(false)
    expect(component.error()).toEqual('Added: Enter a valid date.')
    expect(fixture.debugElement.nativeElement.textContent).toContain(
      'Error while loading documents: Added: Enter a valid date.'
    )
  })

  it('should reload on document consumption finished', () => {
    const fileStatusSubject = new Subject<FileStatus>()
    jest
      .spyOn(websocketStatusService, 'onDocumentConsumptionFinished')
      .mockReturnValue(fileStatusSubject)
    const reloadSpy = jest.spyOn(component, 'reload')
    component.ngOnInit()
    fileStatusSubject.next(new FileStatus())
    expect(reloadSpy).toHaveBeenCalled()
  })

  it('should navigate on showAll', () => {
    const routerSpy = jest.spyOn(router, 'navigate')
    component.showAll()
    expect(routerSpy).toHaveBeenCalledWith(['view', savedView.id])
    savedView.show_in_sidebar = false
    component.showAll()
    expect(routerSpy).toHaveBeenCalledWith(['documents'], {
      queryParams: { view: savedView.id },
    })
  })

  it('should navigate to document', () => {
    const routerSpy = jest.spyOn(router, 'navigate')
    component.openDocumentDetail(documentResults[0])
    expect(routerSpy).toHaveBeenCalledWith(['documents', documentResults[0].id])
  })

  it('should navigate via quickfilter on click tag', () => {
    const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
    component.clickTag(11, new MouseEvent('click'))
    expect(qfSpy).toHaveBeenCalledWith([
      { rule_type: FILTER_HAS_TAGS_ALL, value: '11' },
    ])
    component.clickTag(11) // coverage
  })

  it('should navigate via quickfilter on click correspondent', () => {
    const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
    component.clickCorrespondent(11, new MouseEvent('click'))
    expect(qfSpy).toHaveBeenCalledWith([
      { rule_type: FILTER_CORRESPONDENT, value: '11' },
    ])
    component.clickCorrespondent(11) // coverage
  })

  it('should navigate via quickfilter on click doc type', () => {
    const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
    component.clickDocType(11, new MouseEvent('click'))
    expect(qfSpy).toHaveBeenCalledWith([
      { rule_type: FILTER_DOCUMENT_TYPE, value: '11' },
    ])
    component.clickDocType(11) // coverage
  })

  it('should navigate via quickfilter on click storage path', () => {
    const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
    component.clickStoragePath(11, new MouseEvent('click'))
    expect(qfSpy).toHaveBeenCalledWith([
      { rule_type: FILTER_STORAGE_PATH, value: '11' },
    ])
    component.clickStoragePath(11) // coverage
  })

  it('should navigate via quickfilter on click owner', () => {
    const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
    component.clickOwner(11, new MouseEvent('click'))
    expect(qfSpy).toHaveBeenCalledWith([
      { rule_type: FILTER_OWNER_ANY, value: '11' },
    ])
    component.clickOwner(11) // coverage
  })

  it('should navigate via quickfilter on click more like', () => {
    const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
    component.clickMoreLike(11)
    expect(qfSpy).toHaveBeenCalledWith([
      { rule_type: FILTER_FULLTEXT_MORELIKE, value: '11' },
    ])
  })

  it('should get correct column title', () => {
    expect(component.getColumnTitle(DisplayField.TITLE)).toEqual('Title')
    expect(component.getColumnTitle(DisplayField.CREATED)).toEqual('Created')
    expect(component.getColumnTitle(DisplayField.ADDED)).toEqual('Added')
    expect(component.getColumnTitle(DisplayField.TAGS)).toEqual('Tags')
    expect(component.getColumnTitle(DisplayField.CORRESPONDENT)).toEqual(
      'Correspondent'
    )
    expect(component.getColumnTitle(DisplayField.DOCUMENT_TYPE)).toEqual(
      'Document type'
    )
    expect(component.getColumnTitle(DisplayField.STORAGE_PATH)).toEqual(
      'Storage path'
    )
    expect(component.getColumnTitle(DisplayField.PAGE_COUNT)).toEqual('Pages')
  })

  it('should get correct column title for custom field', () => {
    expect(
      component.getColumnTitle((DisplayField.CUSTOM_FIELD + 11) as any)
    ).toEqual('Custom Field 11')
    expect(
      component.getColumnTitle((DisplayField.CUSTOM_FIELD + 15) as any)
    ).toEqual('Custom Field 15')
  })
})
