import { FindAndCountOptions, Op, Transaction } from 'sequelize'
import {
  AllowNull,
  BelongsTo,
  Column,
  CreatedAt,
  DataType,
  Default,
  ForeignKey,
  Table,
  UpdatedAt
} from 'sequelize-typescript'
import { MLiveVideo } from '@server/types/models/index.js'
import { UserModel } from '../user/user.js'
import { SequelizeModel } from '../shared/index.js'

export enum LiveVideoStatus {
  SCHEDULED = 'scheduled',
  CANCELLED = 'cancelled',
  COMPLETED = 'completed'
}

@Table({
  tableName: 'liveVideo',
  indexes: [
    {
      fields: [ 'targetCamera' ]
    },
    {
      fields: [ 'startTime' ]
    },
    {
      fields: [ 'status' ]
    }
  ]
})
export class LiveVideoModel extends SequelizeModel<LiveVideoModel> {
  @AllowNull(false)
  @Column
  declare title: string

  @AllowNull(true)
  @Column
  declare thumbnailFilename: string | null

  @AllowNull(true)
  @Column(DataType.BLOB)
  declare thumbnailData: Buffer | null

  @AllowNull(true)
  @Column
  declare thumbnailMimeType: string | null

  @AllowNull(true)
  @Column(DataType.BIGINT)
  declare thumbnailSize: number | null

  @AllowNull(true)
  @Column
  declare thumbnailEtag: string | null

  @AllowNull(false)
  @Column
  declare videoOriginalName: string

  @AllowNull(false)
  @Column(DataType.TEXT)
  declare videoStoragePath: string

  @AllowNull(false)
  @Column
  declare videoMimeType: string

  @AllowNull(false)
  @Column(DataType.BIGINT)
  declare videoSize: number

  @AllowNull(false)
  @Column
  declare startTime: Date

  @AllowNull(false)
  @Column
  declare endTime: Date

  @AllowNull(false)
  @Column
  declare targetCamera: string

  @AllowNull(false)
  @Default(LiveVideoStatus.SCHEDULED)
  @Column
  declare status: LiveVideoStatus

  @ForeignKey(() => UserModel)
  @Column
  declare createdByUserId: number

  @BelongsTo(() => UserModel, {
    foreignKey: {
      allowNull: false
    },
    onDelete: 'restrict'
  })
  declare CreatedBy: Awaited<UserModel>

  @CreatedAt
  declare createdAt: Date

  @UpdatedAt
  declare updatedAt: Date

  static listForApi (options: {
    start?: number
    count?: number
    targetCamera?: string
    status?: string
    transaction?: Transaction
  }) {
    const query: FindAndCountOptions = {
      offset: options.start,
      limit: options.count,
      order: [ [ 'startTime', 'DESC' ] ],
      transaction: options.transaction
    }

    const where: Record<string, any> = {}

    if (options.targetCamera) where.targetCamera = options.targetCamera
    if (options.status) where.status = options.status

    if (Object.keys(where).length !== 0) query.where = where

    return LiveVideoModel.findAndCountAll<MLiveVideo>(query)
  }

  static loadById (id: number, transaction?: Transaction) {
    return LiveVideoModel.findByPk<MLiveVideo>(id, { transaction })
  }

  static loadByThumbnailFilename (thumbnailFilename: string, transaction?: Transaction) {
    return LiveVideoModel.findOne<MLiveVideo>({
      where: {
        thumbnailFilename
      },
      transaction
    })
  }

  static listConflicts (options: {
    targetCamera: string
    startTime: Date
    endTime: Date
    excludeId?: number
    transaction?: Transaction
  }) {
    const where: Record<string, any> = {
      targetCamera: options.targetCamera,
      status: {
        [Op.not]: LiveVideoStatus.CANCELLED
      }
    }

    if (options.excludeId) {
      where.id = {
        [Op.ne]: options.excludeId
      }
    }

    return LiveVideoModel.findAll<MLiveVideo>({
      where,
      transaction: options.transaction
    })
  }

  static listForStarting (options: {
    startBefore: Date
    endAfter: Date
    transaction?: Transaction
  }) {
    return LiveVideoModel.findAll<MLiveVideo>({
      where: {
        status: LiveVideoStatus.SCHEDULED,
        startTime: {
          [Op.lte]: options.startBefore
        },
        endTime: {
          [Op.gt]: options.endAfter
        }
      },
      order: [ [ 'startTime', 'ASC' ] ],
      transaction: options.transaction
    })
  }

  toFormattedJSON (this: LiveVideoModel) {
    return {
      id: this.id,
      title: this.title,
      thumbnailFilename: this.thumbnailFilename,
      videoOriginalName: this.videoOriginalName,
      videoStoragePath: this.videoStoragePath,
      videoMimeType: this.videoMimeType,
      videoSize: this.videoSize,
      startTime: this.startTime.toISOString(),
      endTime: this.endTime.toISOString(),
      targetCamera: this.targetCamera,
      status: this.status,
      createdByUserId: this.createdByUserId,
      createdAt: this.createdAt.toISOString(),
      updatedAt: this.updatedAt.toISOString()
    }
  }
}
