// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { ChangeDetectorRef, Component, ViewChild } from '@angular/core';
import { SecurityhubService } from '../../../../../../ng-swagger-gen/services/securityhub.service';
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
import { ClrDatagridStateInterface } from '@clr/angular/data/datagrid/interfaces/state.interface';
import {
    getPageSizeFromLocalStorage,
    PageSizeMapKeys,
    setPageSizeToLocalStorage,
} from '../../../../shared/units/utils';
import { finalize } from 'rxjs/operators';
import { VulnerabilityItem } from '../../../../../../ng-swagger-gen/models/vulnerability-item';
import {
    getDigestLink,
    getRepoLink,
    OptionType,
    SearchEventData,
    severityText,
    VUL_ID,
} from './security-hub.interface';
import { ProjectService } from '../../../../../../ng-swagger-gen/services/project.service';
import { VulnerabilityFilterComponent } from './vulnerability-filter/vulnerability-filter.component';
import { DangerousArtifact } from '../../../../../../ng-swagger-gen/models/dangerous-artifact';
import { PAGE_SIZE_OPTIONS } from 'src/app/shared/entities/shared.const';

@Component({
    selector: 'app-security-hub',
    templateUrl: './security-hub.component.html',
    styleUrls: ['./security-hub.component.scss'],
})
export class SecurityHubComponent {
    clrPageSizeOptions: number[] = PAGE_SIZE_OPTIONS;
    loading: boolean = true;
    currentPage: number = 1;
    pageSize: number = getPageSizeFromLocalStorage(
        PageSizeMapKeys.SECURITY_HUB_VUL,
        10
    );
    total: number = 0;
    vul: VulnerabilityItem[] = [];
    state: ClrDatagridStateInterface;
    options: string[] = [];
    readonly maxNum: number = Number.MAX_SAFE_INTEGER;
    readonly vulId: string = VUL_ID;
    readonly severityText = severityText;
    readonly getDigestLink = getDigestLink;
    readonly getRepoLink = getRepoLink;
    @ViewChild('pagination', { static: true })
    pagination: any;
    @ViewChild(VulnerabilityFilterComponent, { static: true })
    vulnerabilityFilterComponent: VulnerabilityFilterComponent;
    constructor(
        private securityHubService: SecurityhubService,
        private messageHandler: MessageHandlerService,
        private projectService: ProjectService,
        private cd: ChangeDetectorRef
    ) {}

    clrDgRefresh(state: ClrDatagridStateInterface, searchOption: string[]) {
        if (state && state.page) {
            this.pageSize = state.page.size;
            setPageSizeToLocalStorage(
                PageSizeMapKeys.SECURITY_HUB_VUL,
                this.pageSize
            );
        }
        this.loading = true;
        this.state = state;
        this.options = searchOption;
        this.securityHubService
            .ListVulnerabilitiesResponse({
                tuneCount: true,
                withTag: true,
                page: this.currentPage,
                pageSize: this.pageSize,
                q: encodeURIComponent(
                    this.options?.length ? this.options.join(',') : ''
                ),
            })
            .pipe(finalize(() => (this.loading = false)))
            .subscribe({
                next: res => {
                    if (res.headers) {
                        const xHeader: string =
                            res.headers.get('X-Total-Count');
                        if (xHeader) {
                            this.total = parseInt(xHeader, 0);
                            this.cd.detectChanges();
                            this.updateTotalPage();
                        }
                        this.vul = res.body;
                    }
                },
                error: err => {
                    this.messageHandler.error(err);
                },
            });
    }

    search(res: SearchEventData) {
        if (res?.projectId) {
            this.projectService
                .listProjects({
                    name: res.projectId,
                    page: 1,
                    pageSize: 1,
                    withDetail: false,
                })
                .subscribe({
                    next: projects => {
                        if (projects?.length) {
                            res.normal.push(
                                `${OptionType.PROJECT_ID}=${projects[0]?.project_id}`
                            );
                            this.clrDgRefresh(this.state, res?.normal);
                        } else {
                            res.normal.push(`${OptionType.PROJECT_ID}=0`);
                            this.clrDgRefresh(this.state, res?.normal);
                        }
                    },
                    error: err => {
                        res.normal.push(`${OptionType.PROJECT_ID}=0`);
                        this.clrDgRefresh(this.state, res?.normal);
                    },
                });
        } else {
            this.clrDgRefresh(this.state, res?.normal);
        }
    }

    refresh() {
        if (!this.loading) {
            this.currentPage = 1;
            this.clrDgRefresh(this.state, this.options);
        }
    }

    // Use hack way to update total page element
    updateTotalPage() {
        const span: HTMLSpanElement = document.querySelector(
            'app-security-hub clr-datagrid clr-dg-pagination .pagination-list>span'
        );

        const lastPageBtn: HTMLButtonElement = document.querySelector(
            'app-security-hub clr-datagrid clr-dg-pagination .pagination-last'
        );
        if (this.total === -1) {
            if (span) {
                span.innerText = Math.ceil(1000 / this.pageSize) + '+';
            }
            if (lastPageBtn) {
                lastPageBtn.disabled = true;
            }
        } else {
            if (span) {
                span.innerText = Math.ceil(
                    this.total / this.pageSize
                ).toString();
            }
            if (lastPageBtn) {
                lastPageBtn.disabled = false;
            }
        }
    }

    searchCVE(cveId: string) {
        this.vulnerabilityFilterComponent.selectedOptions = [OptionType.CVE_ID];
        this.vulnerabilityFilterComponent.candidates = [
            OptionType.SEVERITY,
            OptionType.CVSS3,
            OptionType.PROJECT_ID,
            OptionType.REPO,
            OptionType.PACKAGE,
            OptionType.TAG,
        ];
        this.vulnerabilityFilterComponent.valueMap = {};
        this.vulnerabilityFilterComponent.valueMap[OptionType.CVE_ID] = cveId;
        this.currentPage = 1;
        this.clrDgRefresh(this.state, [`${OptionType.CVE_ID}=${cveId}`]);
    }
    searchRepo(artifact: DangerousArtifact) {
        this.vulnerabilityFilterComponent.selectedOptions = [
            OptionType.REPO,
            OptionType.DIGEST,
        ];
        this.vulnerabilityFilterComponent.candidates = [
            OptionType.CVE_ID,
            OptionType.SEVERITY,
            OptionType.CVSS3,
            OptionType.PROJECT_ID,
            OptionType.PACKAGE,
            OptionType.TAG,
        ];
        this.vulnerabilityFilterComponent.valueMap = {};
        this.vulnerabilityFilterComponent.valueMap[OptionType.REPO] =
            artifact?.repository_name;
        this.vulnerabilityFilterComponent.valueMap[OptionType.DIGEST] =
            artifact?.digest;
        this.currentPage = 1;
        this.clrDgRefresh(this.state, [
            `${OptionType.REPO}=${artifact?.repository_name}`,
            `${OptionType.DIGEST}=${artifact?.digest}`,
        ]);
    }
}
