/*!
This file is part of CycloneDX JavaScript Library.

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.

SPDX-License-Identifier: Apache-2.0
Copyright (c) OWASP Foundation. All Rights Reserved.
*/

import type { Comparable } from '../../_helpers/sortable'
import { SortableComparables } from '../../_helpers/sortable'
import { CweRepository } from '../../types/cwe'
import { BomRef } from '../bomRef'
import { PropertyRepository } from '../property'
import { Tools } from '../tool'
import { AdvisoryRepository } from './advisory'
import { AffectRepository } from './affect'
import type { Analysis } from './analysis'
import type { Credits } from './credits'
import { RatingRepository } from './rating'
import { ReferenceRepository } from './reference'
import type { Source } from './source'

export interface OptionalVulnerabilityProperties {
  bomRef?: BomRef['value']
  id?: Vulnerability['id']
  source?: Vulnerability['source']
  references?: Vulnerability['references']
  ratings?: Vulnerability['ratings']
  cwes?: Vulnerability['cwes']
  description?: Vulnerability['description']
  detail?: Vulnerability['detail']
  recommendation?: Vulnerability['recommendation']
  advisories?: Vulnerability['advisories']
  created?: Vulnerability['created']
  published?: Vulnerability['published']
  updated?: Vulnerability['updated']
  credits?: Vulnerability['credits']
  tools?: Vulnerability['tools']
  analysis?: Vulnerability['analysis']
  affects?: Vulnerability['affects']
  properties?: Vulnerability['properties']
}

export class Vulnerability implements Comparable<Vulnerability> {
  /** @see bomRef */
  readonly #bomRef: BomRef
  id?: string
  source?: Source
  references: ReferenceRepository
  ratings: RatingRepository
  cwes: CweRepository
  description?: string
  detail?: string
  recommendation?: string
  advisories: AdvisoryRepository
  created?: Date
  published?: Date
  updated?: Date
  credits?: Credits
  tools: Tools
  analysis?: Analysis
  affects: AffectRepository
  properties: PropertyRepository

  constructor (op: OptionalVulnerabilityProperties = {}) {
    this.#bomRef = new BomRef(op.bomRef)
    this.id = op.id
    this.source = op.source
    this.references = op.references ?? new ReferenceRepository()
    this.ratings = op.ratings ?? new RatingRepository()
    this.cwes = op.cwes ?? new CweRepository()
    this.description = op.description
    this.detail = op.detail
    this.recommendation = op.recommendation
    this.advisories = op.advisories ?? new AdvisoryRepository()
    this.created = op.created
    this.published = op.published
    this.updated = op.updated
    this.credits = op.credits
    this.tools = op.tools ?? new Tools()
    this.analysis = op.analysis
    this.affects = op.affects ?? new AffectRepository()
    this.properties = op.properties ?? new PropertyRepository()
  }

  get bomRef (): BomRef {
    return this.#bomRef
  }

  /* eslint complexity: ["error", 50] -- acknowledged  */
  compare (other: Vulnerability): number {
    const bomRefCompare = this.bomRef.compare(other.bomRef)
    if (bomRefCompare !== 0) {
      return bomRefCompare
    }

    /* eslint-disable @typescript-eslint/strict-boolean-expressions -- run compares in weighted order */
    return (this.id ?? '').localeCompare(other.id ?? '') ||
      (this.created?.getTime() ?? 0) - (other.created?.getTime() ?? 0) ||
      (this.published?.getTime() ?? 0) - (other.published?.getTime() ?? 0) ||
      (this.updated?.getTime() ?? 0) - (other.updated?.getTime() ?? 0) ||
      this.ratings.compare(other.ratings) ||
      this.cwes.compare(other.cwes) ||
      (this.description ?? '').localeCompare(other.description ?? '') ||
      (this.detail ?? '').localeCompare(other.detail ?? '') ||
      (this.recommendation ?? '').localeCompare(other.recommendation ?? '') ||
      (this.source && other.source ? this.source.compare(other.source) : 0) ||
      this.properties.compare(other.properties)
    /* eslint-enable  @typescript-eslint/strict-boolean-expressions */
  }
}

export class VulnerabilityRepository extends SortableComparables<Vulnerability> {}
