import MediaImage from "../media/mediaImage.class"
import MediaVideo from "../media/mediaVideo.class"
import MediaAlbum from "../media/mediaAlbum.class"
import MediaIframe from "../media/mediaIframe.class"
import { fulfillMediaFilesForExport } from "../../utils/utilsUserExport"

export default class UserMedia implements IUserMedia {
  public readonly user: IUser

  public structure: IPlatformStructureUserMedia = {
    collections: {}
  }

  public collections: IUserMediaCollections = {}
  private collection: IMediaCollection = 'posts' as IMediaCollection

  constructor(user: IUser) {
    this.user = user
  }

  /**
   * Return available collections from current platform structure
   */
  public get structureCollectionKeys(): IMediaCollection[] {
    return Object.keys(this.structure.collections)
      .map(key => key as IMediaCollection)
  }

  /**
   * Get user active media collections
   */
  public get collectionKeys() {
    return Object.keys(this.collections)
  }

  /**
   * Check if this user has an active collection
   *
   * @param collectionName
   */
  public hasCollection(collectionName: string): boolean {
    return Object.prototype.hasOwnProperty.call(
      this.collections,
      collectionName,
    )
  }

  public setActiveCollection(collection: IMediaCollection) {
    this.collection = collection
  }

  /**
   * Get active collection
   */
  public get activeCollection() {
    return this.collection
  }

  /**
   * Fetch single media from raw media configuration
   *
   * @param collection
   * @param mediaSlug
   */
  public fetchSingleMedia(collection: IMediaCollection, mediaSlug: string) {
    // @ts-expect-error
    const rawMedia = this.user.raw.media[collection].find(rawMedia => rawMedia.slug === mediaSlug)

    if (!rawMedia) {
      return null
    }

    return UserMedia.newMedia(this.user, rawMedia, collection, 'config')
  }

  /**
   * Effectively run the media parsing
   * for each available collection
   *
   * @private
   */
  public importRawUserMediaConfig(from: IMediaFrom = 'config') {
    this.reset()

    for (const collection of this.structureCollectionKeys) {
      if (this.user.raw.media.hasOwnProperty(collection)) {
        for (const rawMedia of this.user.raw.media[collection]) {

          this.addMedia(rawMedia, collection, {
            addMethod: 'push',
            from
          })
        }
      }
    }
  }

  public reset() {
    for (const collection of this.structureCollectionKeys) {
      this.collections[collection] = []
    }
  }

  /**
   * Add a media to specific collection
   *
   * @param rawMedia
   * @param collection
   * @param options
   */
  public addMedia(
    rawMedia: string | IRawMedia | File,
    collection: IMediaCollection = "posts",
    options: {
      addMethod: IMediaAddMethod,
      from: IMediaFrom
    } = {
      addMethod: 'push',
      from: 'config'
    }
  ) {
    const media = UserMedia.newMedia(this.user, rawMedia, collection, options.from)

    this.collections[collection][options.addMethod](media)

    // refresh posts count
    this.user.profile.setPostsCount(this.collections["posts"].length)
  }

  /**
   * Export media
   */
  public async export(): Promise<IUserExportedMedia> {
    const exportedMedia: IUserExportedMedia = {}

    for await (const collectionKey of this.collectionKeys) {
      exportedMedia[collectionKey] = await fulfillMediaFilesForExport(
        this.collections[collectionKey],
      )
    }

    return exportedMedia
  }

  /**
   * Static method that create media instance based on its type
   *
   * @param user
   * @param rawMedia
   * @param collection
   * @param from
   */
  public static newMedia(
    user: IUser,
    rawMedia: string | IRawMedia | File,
    collection: IMediaCollection = "posts",
    from: IMediaFrom = "client"
  ): any {
    const mediaType = UserMedia.detectMediaType(rawMedia)

    switch (mediaType) {
      case "image":
        return new MediaImage(user, rawMedia, collection, from)

      case "video":
        return new MediaVideo(user, rawMedia, collection, from)

      case "album":
        if (typeof rawMedia === "string") {
          throw Error("Album media cannot be a string")
        }

        // @ts-expect-error todo fix types
        return new MediaAlbum(user, rawMedia, collection, from)

      case "iframe":
        if (typeof rawMedia === "string") {
          throw Error("Album media cannot be a string")
        }

        return new MediaIframe(user, rawMedia, collection, from)
    }
  }

  /**
   * Detect media type
   *
   * @param rawMedia
   */
  public static detectMediaType(rawMedia: string | IRawMedia | File): IMediaType {
    let filename = ""

    switch (typeof rawMedia) {
      case "string":
        // shortened media import
        filename = rawMedia
        break

      case "object":
        // shortened album import
        if (Array.isArray(rawMedia)) {
          return "album"
        }

        if (rawMedia.type && rawMedia.type.includes('/')) {
          return rawMedia.type.split('/')[0] as IMediaType
        }

        // regular media import
        if (rawMedia.type) {
          return rawMedia.type as IMediaType
        }

        // fallback
        if (!(rawMedia instanceof File) && rawMedia.file && rawMedia.file) {
          filename = rawMedia.file.name
        } else {
          if (typeof rawMedia.name === "string") {
            filename = rawMedia.name
          }
        }
    }

    switch (getFileExtension(filename)) {
      case "mp4":
        return "video"
      default:
        return "image"
    }
  }
}
