<template>
  <div class="van-uploader" v-if="state.use_wx">
    <div class="van-uploader__wrapper">
      <div class="van-uploader__preview" v-for="(item, index) in pictures" :key="index" @click="preview(index)">
        <van-image :radius="radius" class="van-uploader__preview-image" :src="item.url || item.localData || item.localId" />
        <div v-if="!readonly" role="button" class="van-uploader__preview-delete van-uploader__preview-delete--shadow" tabindex="0" aria-label="删除">
          <van-icon name="cross" class="van-uploader__preview-delete-icon" @click="remove(index)"></van-icon>
        </div>
      </div>
      <template v-if="state.photos.length < limitCount && (showUpload !== false) && !readonly">
        <div class="van-uploader__upload" v-if="!showButton" @click="choose">
          <van-icon name="photograph" class="van-uploader__upload-icon" />
        </div>
        <div class="van-uploader__input-wrapper" v-else>
          <van-button type="primary" size="small" @click="choose" icon="plus">上传图片</van-button>
        </div>
      </template>
    </div>
  </div>
  <template v-else>
    <native-photo-uploader :model-value="state.photos" :show-button="showButton" :readonly="readonly" @update:model-value="update"
      :multiple="multiple" :before-read="beforeRead" :after-read="afterRead" :limit-count="limitCount" :show-upload="showUpload">
    </native-photo-uploader>
  </template>
</template>

<script lang="ts">
import { defineComponent, reactive, getCurrentInstance, watch } from 'vue';
import { Row, Col, Image, Icon, Button, showFailToast, showImagePreview } from 'vant';
import { wx, wxUtil } from "~/utils/wx";
import { WxUploadingPhoto, UploadPhoto } from '~/types'
import NativePhotoUploader from './native-photo-uploader.vue';
import { ImageHelper, isIOS, MediaHelper, bas64ToBlob } from '~/utils'
import {computedAsync} from "@vueuse/core";
import {UpyunAccess} from "~/utils/upyun";

interface PhotoUploaderState {
  photos: Partial<UploadPhoto>[],
  use_wx: boolean
}

export default defineComponent({
  name: "photo-uploader",
  props: {
    readonly: { type: Boolean, default: false },
    multiple: { type: Boolean, default: false },
    maxCount: { type: Number, default: 9 },
    maxSize: { type: Number, default: 5 * 1024 * 1024 },
    radius: { type: Number, default: 5 },
    showButton: { type: Boolean, default: false },
    showUpload: { type: Boolean, default: true },
    doUpload: { type: Boolean, default: true },
    native: { type: Boolean, default: false },
    modelValue: Array<any> // 这行如果不在最后一个，在服务器上build会报错，逗号语法问题
  },
  emits: ['update:modelValue'],
  components: {
    [Row.name]: Row,
    [Col.name]: Col,
    [Image.name]: Image,
    [Icon.name]: Icon,
    [Button.name]: Button,
    [NativePhotoUploader.name]: NativePhotoUploader
  },
  setup(props) {
    const {doUpload, native, maxCount, multiple, modelValue} = props;
    const state = reactive<PhotoUploaderState>({
      use_wx: !native && wxUtil.is_wx_mini(),
      photos: modelValue?.map(x => (x as Partial<UploadPhoto>)) || []
    });
    const instance = getCurrentInstance();

    let limitCount = multiple ? (maxCount || 9) : 1;

    const emitUpdate = (photos?: Partial<UploadPhoto>[]) => {
      instance?.emit("update:modelValue", (photos || state.photos).map(({url, ...rest}) => ({url: url?.split('?')[0], ...rest})))
    }

    const getLocalImg = async (localId: string) => {
      return new Promise((resolve, reject) => {
        console.log(1, "start.. get local image")
        wx.getLocalImgData({
          localId,
          isShowProgressTips: 0,
          success: (res: any) => {
            console.log(1, "get localdata done")
            resolve(res.localData)
          },
          fail: (err: any) => {
            console.log(1, "get local data err...", err)
            reject(err)
          },
          complete: (r: any) => {
            console.log(1, "complete...", r)
          }
        })
      })
    }

    const pictures = computedAsync(async () => {
      return await Promise.all(props.modelValue?.map(async ({wx_cache, ...rest}) => {
        let authorized = await MediaHelper.authorize(rest);
        let {localId, localData, serverId} = wx_cache || {};
        if (serverId && !localId && wxUtil.is_wx_mini()) {
          try {
            localId = await wxUtil.download_image(serverId);
            if (isIOS()) {
              localData = await getLocalImg(localId);
              // if (!localData.startsWith("data:image")) {
              //   // in android, the localData does not include this prefix
              //   localData = "data:image/jpeg;base64," + localData
              // }
            }
          } catch (e) {
            console.error("get image from wx server failed...", e);
          }
        }
        return {
          ...authorized, localId, localData, serverId
        }
      })) || [];
    })

    watch(() => props.modelValue, async () => {
      state.photos = await Promise.all(props.modelValue?.map(async x => (await MediaHelper.authorize(x))) || [])
    })


    wxUtil.init(['chooseImage', 'previewImage', 'uploadImage', 'downloadImage', 'getLocalImgData']);

    const uploadToWx = (localIds: string[]) => {
      let localId = localIds.pop();
      wx.getLocalImgData({
        localId,
        success: ({localData}: any) => {
          let blob = bas64ToBlob(localData, "image/jpeg");
          if (blob.size > 5 * 1024 * 1024) {
            showFailToast('文件大小不能超过5MB!');
            return;
          }
          wx.uploadImage({
            localId,
            isShowProgressTips: 1,
            success: async function ({ serverId }: any) {
              let item: Partial<WxUploadingPhoto> = { localId, serverId };
              if (doUpload) {
                // 直接上传到upyun，不需要拼接的情况
                let r = await ImageHelper.upload_by_wx(serverId)
                if (r) {
                  state.photos.push(await MediaHelper.authorize(r));
                  emitUpdate()
                } else {
                  showFailToast("文件上传失败！")
                }
              } else {
                // get localData to preview
                // if (!localData.startsWith("data:image")) {
                //   // in android, the localData does not include this prefix
                //   localData = "data:image/jpeg;base64," + localData
                // }
                if (isIOS()) {
                  item.localData = localData;
                }
                state.photos.push(item);
                emitUpdate();
              }

              if (localIds.length > 0) {
                // 递归调用，通过数组遍历的方式重复调用是不生效的！
                uploadToWx(localIds);
              }
            }
          });
        },
        fail: (err: any) => {
          console.log("get local image data failed...", err)
        }
      })
    }

    const choose = () => {
      wx.chooseImage({
        count: limitCount - state.photos.length,
        sizeType: ['original', 'compressed'],
        sourceType: ['album', 'camera'],
        success: function ({ localIds }: any) {
          uploadToWx(localIds);
        },
        fail: function (res: any) {
          console.log("chooseImage failed", res)
        }
      });
    }

    const preview = async (index: number) => {
      let images = await Promise.all(pictures.value.map(async (p: any) => {
        return p.url || p.localData || p.localId;
      }).filter(p => !!p))
      if (images.length > 0) {
        showImagePreview({images, startPosition: index})
      }
    }

    const remove = async (index: number) => {
      await MediaHelper.remove(state.photos.slice(index, 1))
      state.photos.splice(index, 1)
      emitUpdate()
    }


    const beforeRead = (file: File) => {
        if (!file.type.startsWith("image")) {
            showFailToast('请上传图片文件！');
            return false;
        }
        return true;
    };

    const afterRead = async (file: any) => {
      // when set doUpload=false and native=false but we actually fallback to native, we think it's doUpload=true
      if (doUpload || (!native && !wxUtil.is_wx_mini())) {
        const r = await ImageHelper.upload_by_form(file)
        if (r) {
          state.photos.push(await MediaHelper.authorize(r));
          emitUpdate()
        }
      }
    };

    const update = (photos: Partial<UploadPhoto>[]) => {
      if ((!doUpload && native) || !photos.some(p => !p.url)) {
        emitUpdate(photos)
      }
    }
    

    return {
      state, choose, preview, remove, afterRead, limitCount, update, beforeRead, pictures
    }
  }
})
</script>
<style lang="less">

</style>
