<template>
  <wrapper
    class="c-step"
    fill
    margin
    padding
    background
  >
    <div class="l-flex__none l-flex--row c-step__header">
      <button
        class="l-flex__none c-sibling-item o-button"
        @click="onPresent"
      >
        导入历史编单
      </button>
      <div class="l-flex__fill u-text--center">
        请选择需要上播的设备并配置相关上播内容
      </div>
      <button
        class="l-flex__none c-sibling-item o-button"
        @click="onNext"
      >
        发布
      </button>
    </div>
    <div class="l-flex__fill l-flex">
      <device-tree
        ref="tree"
        class="c-sibling-item c-step__column u-width--md"
        exact
        checkbox
        check-on-click-node
        @change="onChange"
      />
      <div
        class="l-flex__none l-flex--col c-step__column"
        style="width: 360px"
      >
        <div class="c-sibling-item--v near u-font-size--sm u-bold">
          上播类型
        </div>
        <el-select
          v-model="eventType"
          class="c-sibling-item--v nearer u-width--sm"
          size="small"
        >
          <el-option
            v-for="option in eventTypeOptions"
            :key="option.value"
            :label="option.label"
            :value="option.value"
          />
        </el-select>
        <template v-if="isNormal">
          <div class="c-sibling-item--v near u-font-size--sm">
            优先级
          </div>
          <el-select
            v-model="priority"
            class="c-sibling-item--v nearer u-width--sm"
            size="small"
          >
            <el-option
              v-for="option in priorityOptions"
              :key="option.value"
              :label="option.label"
              :value="option.value"
            />
          </el-select>
          <div class="l-flex--row c-sibling-item--v near">
            <div
              class="l-flex__none l-flex--row inline c-sibling-item u-font-size--sm has-active"
              @click="onAddEvent"
            >
              <span class="c-sibling-item">
                播放时段
              </span>
              <i class="c-sibling-item near el-icon-circle-plus-outline" />
            </div>
          </div>
          <schema-table
            ref="timeTable"
            class="c-sibling-item--v nearer"
            :schema="timeSchema"
          />
        </template>
        <template v-if="isIntercut">
          <div class="c-sibling-item--v near u-font-size--sm u-required">
            下架日期
            <span class="u-color--info u-font-size--xs">
              下架当日插播依然生效
            </span>
          </div>
          <el-date-picker
            v-model="intercutFinishDate"
            class="c-sibling-item--v nearer u-width--sm"
            value-format="yyyy-MM-dd"
            placeholder="选择日期"
            :clearable="false"
            :picker-options="pickerOptions"
          />
        </template>
      </div>
      <event-target-picker
        ref="eventTargetPicker"
        class="l-flex__fill c-step__column"
        :event-target="eventTarget"
        :only-assets="isIntercut"
      />
    </div>
    <radio-table-dialog
      ref="workflowTableDialog"
      title="历史编单"
      size="lg"
      :schema="workflowSchema"
      @confirm="onChooseWorkflow"
    />
    <workflow-dialog ref="workflowDialog" />
    <intercut-workflow-dialog ref="intercutWorkflowDialog" />
    <event-frequency-config-dialog
      ref="eventFrequencyConfigDialog"
      @confirm="onConfirmEventFrequency"
    />
    <confirm-dialog
      ref="selfConflictDialog"
      title="冲突提醒"
      cancel-text="重新编辑"
      confirm-text="覆盖"
      append-to-body
      @confirm="onCover"
    >
      <div
        v-for="conflict in selfConflicts"
        :key="conflict.key"
      >
        {{ conflict.info }}
      </div>
    </confirm-dialog>
    <c-dialog
      ref="priorityConflictDialog"
      title="冲突数据"
      size="medium"
      append-to-body
    >
      <div class="c-sibling-item--v u-color--error">
        以下设备将与新发布的时段存在冲突
      </div>
      <schema-table
        class="c-sibling-item--v nearer sm"
        :schema="priorityConflictSchema"
      />
    </c-dialog>
    <confirm-dialog
      ref="conflictDialog"
      title="冲突数据提醒"
      size="medium"
      cancel-text="重新编辑"
      confirm-text="覆盖"
      append-to-body
      @confirm="onCoverConfilcts"
    >
      <schema-table :schema="conflictSchema" />
    </confirm-dialog>
  </wrapper>
</template>

<script>
import {
  ONE_DAY,
  State,
  EventTarget,
  WorkflowType,
  PublishType,
  PublishTargetType,
  EventFrequency,
  PlayPriority,
  PlayPriorityInfo
} from '@/constant.js'
import {
  toDateStr,
  getConflict,
  getEventDescription
} from '@/utils/event.js'
import {
  publish,
  getPublishConflicts,
  publishIntercut
} from '@/api/platform.js'
import {
  getWorkflowsByUser,
  getWorkflowsBySuperAdmin,
  getWorkflowDetail
} from '@/api/workflow.js'
import EventFrequencyConfigDialog from '../components/EventFrequencyConfigDialog.vue'
import WorkflowDialog from '../workflow/components/WorkflowDialog.vue'
import IntercutWorkflowDialog from '../workflow/components/IntercutWorkflowDialog.vue'
import PriorityChecking from './components/PriorityChecking.vue'

const PublishDataType = {
  NORMAL: 'NORMAL',
  DEFAULT_PLAYBACK: 'DEFAULT_PLAYBACK',
  INTERCUT: 'INTERCUT'
}

export default {
  name: 'DeployDevice',
  components: {
    EventFrequencyConfigDialog,
    WorkflowDialog,
    IntercutWorkflowDialog
  },
  data () {
    return {
      selectedDevices: [],
      eventType: PublishDataType.NORMAL,
      eventTypeOptions: [
        { value: PublishDataType.NORMAL, label: '常规' },
        { value: PublishDataType.INTERCUT, label: '资源插播' },
        { value: PublishDataType.DEFAULT_PLAYBACK, label: '默认播放' }
      ],
      priority: PlayPriority.FIRST,
      priorityOptions: [
        {
          value: PlayPriority.FIRST,
          label: PlayPriorityInfo[PlayPriority.FIRST]
        },
        {
          value: PlayPriority.SECOND,
          label: PlayPriorityInfo[PlayPriority.SECOND]
        },
        {
          value: PlayPriority.THIRD,
          label: PlayPriorityInfo[PlayPriority.THIRD]
        },
        {
          value: PlayPriority.FOURTH,
          label: PlayPriorityInfo[PlayPriority.FOURTH]
        },
        {
          value: PlayPriority.FIFTH,
          label: PlayPriorityInfo[PlayPriority.FIFTH]
        }
      ],
      intercutFinishDate: null,
      timeSchema: {
        nonPagination: true,
        props: {
          size: 'small',
          'row-key': 'key'
        },
        list: this.getEvents,
        cols: [
          {
            label: '在播',
            render: ({ origin }, h) => h(PriorityChecking, {
              props: {
                deviceIds: this.selectedDeviceIds,
                priority: this.priority,
                event: origin
              },
              on: {
                click: this.onViewPriorityConflicts
              }
            }),
            width: 84,
            align: 'center'
          },
          { prop: 'time', label: '生效时间', 'show-overflow-tip': false },
          {
            type: 'invoke',
            render: [{ label: '移除', on: this.removeEventProxy }],
            width: 52
          }
        ]
      },
      priorityConflicts: [],
      priorityConflictSchema: {
        list: this.getPriorityConflicts,
        props: {
          size: 'small'
        },
        cols: [
          { prop: 'deviceName', label: '设备名称' },
          { prop: 'name', label: '上播内容' },
          { prop: 'priority', label: '优先级', render: row => PlayPriorityInfo[row.priority], size: 'sm', width: 84, align: 'center' },
          { prop: 'start', label: '开始时间', width: 140, align: 'center' },
          { prop: 'until', label: '结束时间', width: 140, align: 'center' }
        ]
      },
      conflicts: [],
      conflictSchema: {
        list: this.getConflicts,
        props: {
          size: 'small'
        },
        cols: [
          { prop: 'deviceName', label: '设备名称' },
          { prop: 'name', label: '上播内容' },
          { prop: 'priority', label: '优先级', render: row => PlayPriorityInfo[row.priority], size: 'sm', width: 84, align: 'center' },
          { prop: 'start', label: '开始时间', width: 140, align: 'center' },
          { prop: 'until', label: '结束时间', width: 140, align: 'center' }
        ]
      },
      events: [],
      selfConflicts: [],
      eventTarget: this.createEventTarget(),
      workflowSchema: {
        props: {
          size: 'small'
        },
        list: this.$store.getters.isSuperAdmin ? getWorkflowsBySuperAdmin : getWorkflowsByUser,
        filters: [
          { key: 'status', type: 'select', placeholder: '流程状态', options: [
            { value: State.SUBMITTED, label: '待审核' },
            { value: State.RESOLVED, label: '通过' },
            { value: State.REJECTED, label: '驳回' }
          ] },
          { key: 'flowName', type: 'search', placeholder: '上播内容' },
          { type: 'refresh' }
        ],
        cols: [
          { prop: 'flowDesc', label: '优先级', width: 120, align: 'center' },
          { prop: 'flowName', label: '上播内容' },
          { label: '状态', type: 'tag', render: ({ status }) => {
            return {
              type: ['', 'primary', 'success', 'danger'][status],
              label: ['', '待审核', '通过', '驳回'][status]
            }
          } },
          this.$store.getters.isSuperAdmin && { prop: 'createUser', label: '提交人', width: 140, align: 'center' },
          { prop: 'createTime', label: '提交时间', width: 140, align: 'center' },
          { type: 'invoke', render: [
            { label: '详情', on: this.onViewWorkflow }
          ] }
        ]
      }
    }
  },
  computed: {
    isNormal () {
      return this.eventType === PublishDataType.NORMAL
    },
    isDefaultPlayback () {
      return this.eventType === PublishDataType.DEFAULT_PLAYBACK
    },
    isIntercut () {
      return this.eventType === PublishDataType.INTERCUT
    },
    selectedDeviceIds () {
      return this.selectedDevices.map(device => device.id)
    },
    pickerOptions () {
      return {
        disabledDate: date => date <= Date.now() - ONE_DAY
      }
    }
  },
  methods: {
    createEventTarget (options) {
      return { type: EventTarget.ASSETS, ...options }
    },
    onPresent () {
      this.$refs.workflowTableDialog.show()
    },
    onNext () {
      const devices = this.selectedDevices
      const length = devices.length
      if (!length) {
        this.$message({
          type: 'warning',
          message: '请选择目标设备'
        })
        return
      }
      switch (this.eventType) {
        case PublishDataType.NORMAL:
          this.onPublishNormal()
          break
        case PublishDataType.DEFAULT_PLAYBACK:
          this.onPublishDefaultPlayback()
          break
        case PublishDataType.INTERCUT:
          this.onPublishIntercut()
          break
        default:
          break
      }
    },
    onChange (devices) {
      this.selectedDevices = Object.freeze(devices)
    },
    getEvents () {
      return Promise.resolve({ data: this.events })
    },
    getPriorityConflicts () {
      return Promise.resolve({ data: this.priorityConflicts })
    },
    getConflicts () {
      return Promise.resolve({ data: this.conflicts })
    },
    onAddEvent () {
      this.$refs.eventFrequencyConfigDialog.show()
    },
    onConfirmEventFrequency ({ value, done }) {
      this.$mergedValue = value
      const selfConflicts = new Map()
      if (this.events.length) {
        this.events.forEach(eventProxy => {
          value.forEach(event => {
            const date = getConflict(event, eventProxy.origin)
            if (date) {
              selfConflicts.set(eventProxy.key, {
                key: eventProxy.key,
                info: `与 ${eventProxy.time} 有冲突`
              })
            }
          })
        })
      }
      if (selfConflicts.size) {
        this.selfConflicts = [...selfConflicts.values()]
        this.$conflictOptions = { value, done }
        this.$refs.selfConflictDialog.show()
        return
      }
      this.onAdded(value, done)
    },
    onCover (closeCb) {
      this.selfConflicts.forEach(this.removeEventProxy)
      const { value, done } = this.$conflictOptions
      this.$conflictOptions = null
      closeCb()
      this.onAdded(value, done)
    },
    getDeviceName (deviceId) {
      if (!this._deviceNameCache) {
        this._deviceNameCache = {}
      }
      if (!this._deviceNameCache[deviceId]) {
        this._deviceNameCache[deviceId] = this.selectedDevices.find(device => device.id === deviceId)?.name || '-'
      }
      return this._deviceNameCache[deviceId]
    },
    onViewPriorityConflicts (conflicts) {
      this.priorityConflicts = conflicts.map(item => {
        const { deviceId, name, priority, start, until } = item
        return {
          deviceName: this.getDeviceName(deviceId),
          name, priority, start, until
        }
      })
      this.$refs.priorityConflictDialog.show()
    },
    onAdded (eventList, done) {
      this.events = [
        ...eventList.map(event => {
          return {
            key: `${Date.now()}_${Math.random()
              .toString(16)
              .slice(2)}`,
            origin: event,
            time: getEventDescription(event)
          }
        }),
        ...this.events
      ]
      this.$refs.timeTable?.pageTo()
      done()
    },
    removeEventProxy (eventProxy) {
      if (eventProxy) {
        const { key } = eventProxy
        const index = this.events.findIndex(event => event.key === key)
        if (~index) {
          this.events.splice(index, 1)
          return index
        }
      }
      return -1
    },
    onViewWorkflow ({ id, type }) {
      if (type === WorkflowType.INTERCUT) {
        this.$refs.intercutWorkflowDialog.show(id)
      } else {
        this.$refs.workflowDialog.show(id)
      }
    },
    onChooseWorkflow ({ value: { id, type }, done }) {
      getWorkflowDetail(id, { loading: true }).then(({ data }) => {
        let devices = []
        if (type === WorkflowType.INTERCUT) {
          this.eventType = PublishDataType.INTERCUT
          const today = toDateStr(Date.now())
          this.intercutFinishDate = today <= data.endDate
            ? data.endDate
            : null

          this.eventTarget = this.createEventTarget({
            name: data.name,
            sources: data.deviceCalendarAdditionMediaDtoList.map(media => {
              return {
                md5: media.md5,
                tag: media.tag,
                name: media.originalName,
                size: media.size,
                type: media.type,
                keyName: media.keyName,
                duration: media.addDuration
              }
            })
          })

          devices = data.deviceIdList.map((deviceId, index) => {
            return {
              deviceId,
              deviceName: data.deviceNameList[index]
            }
          })
        } else {
          const {
            programCalendarName,
            calendarReleaseDeviceList,
            targetList
          } = data
          const targets = targetList.map(JSON.parse)
          const { detail: { priority, target } } = targets[0]

          this.eventType = priority === PlayPriority.FIRST
            ? PublishDataType.DEFAULT_PLAYBACK
            : PublishDataType.NORMAL
          this.priority = priority

          this.events = targets.map(
            ({ detail: { priority, target, ...event } }) => {
              return {
                key: `${Date.now()}_${Math.random()
                  .toString(16)
                  .slice(2)}`,
                origin: event,
                time: getEventDescription(event)
              }
            }
          )
          this.$refs.timeTable?.pageTo(1)

          this.eventTarget = target.type === EventTarget.ASSETS
            ? {
              name: programCalendarName,
              ...this.createEventTarget(target)
            }
            : this.createEventTarget(target)

          devices = calendarReleaseDeviceList
        }
        const deviceCount = devices.length
        const validDevices = devices.filter(({ deviceName }) => !!deviceName)
        const validDeviceCount = validDevices.length
        const diffDeviceCount = this.$refs.tree.reset(validDevices.map(({ deviceId }) => deviceId))
        console.log(
          'deploy import',
          diffDeviceCount,
          validDeviceCount,
          deviceCount
        )
        if (diffDeviceCount > 0 || validDeviceCount !== deviceCount) {
          this.$message({
            type: 'warning',
            message: '部分设备无操作权限或已移除，请确认设备'
          })
        }
        done()
      })
    },
    getPublishDescription (value = '', warningTip = '') {
      return this.$prompt(
        `<p>发布后需审核生效，操作完成后请通知相关人员进行审核</p>${warningTip}<br/><p class="u-color--black u-font-size--sm u-bold">编单名称</p>`,
        '操作确认',
        {
          dangerouslyUseHTMLString: true,
          closeOnClickModal: false,
          inputPlaceholder: '发布内容的简单描述',
          inputValue: value,
          inputPattern: /^.{1,30}$/,
          inputErrorMessage: '请用1~30个字符进行描述',
          confirmButtonText: '发布',
          cancelButtonText: '取消'
        }
      )
    },
    onPublishNormal () {
      return this.getNoramlPublishData().then(
        ({ publishType, targetList, name, resolutionRatio }) => {
          const warningTip = resolutionRatio && (!this.ratio || resolutionRatio !== this.ratio)
            ? '<p class="u-color--error">上播内容与设备分辨率不一致，可能无法完全适配</p>'
            : ''
          return this.getPublishDescription(name, warningTip).then(({ value }) => getPublishConflicts({
            deviceIdList: this.selectedDevices.map(device => device.id),
            conflictCheckTimeList: targetList.map(({ detail: { freq, start, until, byDay, startTime, endTime } }) => {
              return { freq, start, until, byDay, startTime, endTime }
            }),
            minPriority: this.priority,
            maxPriority: this.priority
          }).then(({ data }) => {
            const publishData = {
              publishType,
              targetList,
              options: {
                programCalendarName: value,
                resolutionRatio: resolutionRatio || '自适应',
                remark: PlayPriorityInfo[this.priority]
              }
            }
            if (data && data.length > 0) {
              this._cachePublishData = publishData
              this.showConflicts(data)
            } else {
              this.conflicts = []
              this.onSubmitPublish(publishData)
            }
          }))
        }
      )
    },
    showConflicts (data) {
      this.conflicts = data.map(item => {
        const { deviceId, name, priority, start, until } = item.deviceCalendarRecordCO
        return {
          deviceName: this.getDeviceName(deviceId),
          name, priority, start, until
        }
      })
      this.$refs.conflictDialog.show()
    },
    onCoverConfilcts (done) {
      this.onSubmitPublish(this._cachePublishData).then(done)
    },
    getNoramlPublishData () {
      if (!this.events.length) {
        this.$message({
          type: 'warning',
          message: '请添加上播时段'
        })
        return Promise.reject('invalid event, no time')
      }
      const eventTarget = this.$refs.eventTargetPicker.getValue()
      if (!eventTarget) {
        return Promise.reject('invalid event, no target')
      }
      const { detail } = this.$refs.eventTargetPicker.getSnapshot()
      const type = PublishTargetType.EVENT
      const priority = this.priority
      return Promise.resolve({
        publishType: eventTarget.type === EventTarget.ASSETS
          ? PublishType.ASSET_TO_DEVICE
          : PublishType.PROGRAM_TO_DEVICE,
        targetList: this.events.map(({ origin }) => {
          return {
            type,
            detail: {
              priority,
              ...origin,
              target: eventTarget
            }
          }
        }),
        name: detail?.name || '',
        resolutionRatio: detail?.resolutionRatio
      })
    },
    onPublishDefaultPlayback () {
      return this.getDefaultPlaybackPublishData().then(
        ({ publishType, targetList, name, resolutionRatio }) => {
          const warningTip = resolutionRatio && (!this.ratio || resolutionRatio !== this.ratio)
            ? '<p class="u-color--error">上播内容与设备分辨率不一致，可能无法完全适配</p>'
            : ''
          return this.getPublishDescription(name, warningTip).then(({ value }) => this.onSubmitPublish({
            publishType,
            targetList,
            options: {
              programCalendarName: value,
              resolutionRatio: resolutionRatio || '自适应',
              remark: PlayPriorityInfo[PlayPriority.FIRST]
            }
          }))
        }
      )
    },
    getDefaultPlaybackPublishData () {
      const eventTarget = this.$refs.eventTargetPicker.getValue()
      if (!eventTarget) {
        return Promise.reject('invalid event, no target')
      }
      const { detail } = this.$refs.eventTargetPicker.getSnapshot()
      return Promise.resolve({
        publishType: eventTarget.type === EventTarget.ASSETS
          ? PublishType.ASSET_TO_DEVICE
          : PublishType.PROGRAM_TO_DEVICE,
        targetList: [{
          type: PublishTargetType.EVENT,
          detail: {
            priority: PlayPriority.FIRST,
            freq: EventFrequency.ONCE,
            start: `${toDateStr(new Date())} 00:00:00`,
            target: eventTarget
          }
        }],
        name: detail?.name || '',
        resolutionRatio: detail?.resolutionRatio
      })
    },
    onSubmitPublish ({ publishType, targetList, options }) {
      return publish(
        publishType,
        this.selectedDevices.map(device => device.id),
        targetList,
        options
      ).then(() => {
        this.$refs.tree.reset()
        this.eventTarget = this.createEventTarget()
      })
    },
    onPublishIntercut () {
      return this.getIntercutPublishData().then(
        ({ name, ...data }) => this.getPublishDescription(name).then(
          ({ value }) => publishIntercut({
            name: value,
            ...data
          })
        )
      ).then(() => {
        this.$refs.tree.reset()
        this.eventTarget = this.createEventTarget()
      })
    },
    getIntercutPublishData () {
      if (!this.intercutFinishDate) {
        this.$message({
          type: 'warning',
          message: '请选择下架日期'
        })
        return Promise.reject('invalid event, no time')
      }

      const eventTarget = this.$refs.eventTargetPicker.getValue()
      if (!eventTarget) {
        return Promise.reject('invalid event, no target')
      }

      const { detail } = this.$refs.eventTargetPicker.getSnapshot()
      return Promise.resolve({
        startDate: toDateStr(Date.now()),
        endDate: this.intercutFinishDate,
        deviceIdList: this.selectedDevices.map(device => device.id),
        mediaInfoDtoList: eventTarget.sources.map(item => {
          return {
            keyName: item.keyName,
            duration: item.duration
          }
        }),
        name: detail?.name || ''
      })
    }
  }
}
</script>
