import type { DataCatalogLifecycle } from './DataCatalogVersion.js'
import { DataCatalogKind } from './kinds.js'

export type DataCatalogScope = 'public' | 'private' | 'organization'

export interface DataCatalogSchema {
  /**
   * The key of the data catalog entry.
   * When the value is missing it will be generated by the API.
   *
   * The key can only be set when creating a new data catalog entry.
   * @readonly
   */
  key: string
  /**
   * The kind of the schema
   * This is set by the API and is used to identify the schema.
   * @readonly
   */
  readonly kind: typeof DataCatalogKind
  /**
   * The key of the organization.
   * @required
   */
  organization: string
  /**
   * The key of the related data catalog file entry.
   * @required
   */
  file: string
  /**
   * The scope of the data catalog entry.
   * @required
   */
  scope: DataCatalogScope
  /**
   * The name of the data catalog entry.
   * It is required when creating a new data catalog entry.
   * @required
   */
  name: string
  /**
   * A short description of the data catalog entry.
   * @required
   */
  description: string
  /**
   * The id of the user who created the data catalog entry.
   * It is ignored when creating or updating a data catalog entry.
   * @readonly
   */
  publishedBy: string
  /**
   * The time when the last version was published.
   * It is ignored when creating or updating a data catalog entry.
   * @readonly
   */
  publishedAt: number
  /**
   * The time when the data catalog entry was created.
   * It is ignored when creating or updating a data catalog entry.
   * @readonly
   */
  createdAt: number
  /**
   * The time when the data catalog entry was last updated.
   * It is ignored when creating or updating a data catalog entry.
   * @readonly
   */
  updatedAt: number
  /**
   * The timestamp when this entry was unpublished.
   * Only set when the entry was unpublished.
   * @readonly
   */
  unpublishedAt?: number
  /**
   * A flag indicating if the entry is deprecated.
   * @readonly
   */
  deprecated?: boolean
  /**
   * The timestamp when the entry was deprecated.
   * Only set when the entry was deprecated.
   * @readonly
   */
  deprecatedAt?: number
  /**
   * The id of the user who deprecated the entry.
   * Only set when the entry was deprecated.
   * @readonly
   */
  deprecatedBy?: string
  /**
   * The reason why the entry was deprecated.
   * Only set when the entry was deprecated.
   */
  deprecationReason?: string
  /**
   * The list of tags associated with the data catalog entry.
   * @readonly Use the API endpoints to manage tags.
   */
  tags: string[]
}

export interface DataCatalogSchemaVersion {
  /**
   * The version of the data catalog entry.
   */
  version: string
  /**
   * The lifecycle of the data catalog version.
   */
  lifecycle: DataCatalogLifecycle
}

/**
 * Used with API communication when listing data catalog entries.
 */
export interface DataCatalogSchemaWithVersion extends DataCatalogSchema {
  /**
   * The list of published versions of the data catalog entry.
   * Note, this is limited to the last 20 versions.
   */
  versions: DataCatalogSchemaVersion[]
}

/**
 * Simplified information about a data catalog version.
 * It is used when listing versions of a data catalog entry.
 */
export interface DataCatalogVersionInfo {
  /**
   * The key of the version.
   */
  key: string
  /**
   * The version string according to the [semantic versioning](https://semver.org/) specification.
   * Note, this value is limited to 32 characters by the API.
   */
  version: string
  /**
   * The timestamp when this version was published.
   */
  published: number
}

export interface DataCatalogStatus extends DataCatalogSchema {
  versions: DataCatalogVersionInfo[]
}

export class DataCatalog implements DataCatalogSchema {
  key: string

  get kind(): typeof DataCatalogKind {
    return DataCatalogKind
  }

  organization: string
  file: string
  scope: DataCatalogScope
  name: string
  description: string
  publishedBy: string
  publishedAt: number
  createdAt: number
  updatedAt: number
  unpublishedAt?: number | undefined
  deprecated?: boolean | undefined
  deprecatedAt?: number | undefined
  deprecatedBy?: string | undefined
  deprecationReason?: string | undefined
  tags: string[]

  static createSchema(input: Partial<DataCatalogSchema>): DataCatalogSchema {
    const {
      key = '',
      organization = '',
      file = '',
      scope = 'private',
      name = '',
      description = '',
      publishedBy = '',
      publishedAt = 0,
      createdAt = 0,
      updatedAt = 0,
      tags = [],
    } = input
    if (['public', 'private', 'organization'].indexOf(scope) === -1) {
      throw new Error(`Invalid scope: ${scope}`)
    }
    const result: DataCatalogSchema = {
      key,
      kind: DataCatalogKind,
      organization,
      file,
      scope,
      name,
      description,
      publishedBy,
      publishedAt,
      createdAt,
      updatedAt,
      tags,
    }
    if (typeof input.deprecated === 'boolean') {
      result.deprecated = input.deprecated
    }
    if (typeof input.deprecatedAt === 'number') {
      result.deprecatedAt = input.deprecatedAt
    }
    if (typeof input.deprecatedBy === 'string') {
      result.deprecatedBy = input.deprecatedBy
    }
    if (typeof input.deprecationReason === 'string') {
      result.deprecationReason = input.deprecationReason
    }
    if (typeof input.unpublishedAt === 'number') {
      result.unpublishedAt = input.unpublishedAt
    }
    return result
  }

  constructor(input: Partial<DataCatalogSchema> = {}) {
    const init = DataCatalog.createSchema(input)
    this.key = init.key
    this.organization = init.organization
    this.file = init.file
    this.scope = init.scope
    this.name = init.name
    this.description = init.description
    this.publishedBy = init.publishedBy
    this.publishedAt = init.publishedAt
    this.createdAt = init.createdAt
    this.updatedAt = init.updatedAt
    this.tags = init.tags
    if (typeof init.deprecated === 'boolean') {
      this.deprecated = init.deprecated
    }
    if (typeof init.deprecatedAt === 'number') {
      this.deprecatedAt = init.deprecatedAt
    }
    if (typeof init.deprecatedBy === 'string') {
      this.deprecatedBy = init.deprecatedBy
    }
    if (typeof init.deprecationReason === 'string') {
      this.deprecationReason = init.deprecationReason
    }
    if (typeof init.unpublishedAt === 'number') {
      this.unpublishedAt = init.unpublishedAt
    }
  }

  toJSON(): DataCatalogSchema {
    const result: DataCatalogSchema = {
      key: this.key,
      kind: DataCatalogKind,
      organization: this.organization,
      file: this.file,
      scope: this.scope,
      name: this.name,
      description: this.description,
      publishedBy: this.publishedBy,
      publishedAt: this.publishedAt,
      createdAt: this.createdAt,
      updatedAt: this.updatedAt,
      tags: [...this.tags],
    }
    if (typeof this.deprecated === 'boolean') {
      result.deprecated = this.deprecated
    }
    if (typeof this.deprecatedAt === 'number') {
      result.deprecatedAt = this.deprecatedAt
    }
    if (typeof this.deprecatedBy === 'string') {
      result.deprecatedBy = this.deprecatedBy
    }
    if (typeof this.deprecationReason === 'string') {
      result.deprecationReason = this.deprecationReason
    }
    if (typeof this.unpublishedAt === 'number') {
      result.unpublishedAt = this.unpublishedAt
    }
    return result
  }
}
