import JSZip from 'jszip';
import { s8 } from './random';
import axios from 'axios';
import CryptoJS from 'crypto-js';
import { deepClone } from '@meta2d/core';

export const MESH_SUFFIXES = ['.glb', '.l3db'];
export const TEMPLATE_SUFFIXES = ['.l3dm'];
export const IMAGE_SUFFIXES = ['.png', '.jpg', '.jpeg', '.svg', '.bmp', '.gif'];
export const HDR_SUFFIXES = ['.hdr', '.dds', '.env'];

export async function load3d(zip: JSZip, key: string) {
  const _filename = key.split('/')[0];
  const dataStr = await zip.file(key).async('string');
  const { data, map } = JSON.parse(dataStr);
  const newUrlMap: { [oldUrl: string]: string } = {};
  await Promise.all(
    Object.keys(map).map(async (url) => {
      try {
        const file = await zip
          .file(_filename + '/files/' + map[url])
          ?.async('arraybuffer');
        if (file) {
          const names = filename(url, true).split('.');
          names.splice(-1, 0, s8());
          const name = names.join('.');
          const newFile = new File([file], name);
          const suffix = '.' + names[names.length - 1];
          let directory = '';
          let type = '';
          let tags = '';
          if (MESH_SUFFIXES.includes(suffix)) {
            directory = '/3D/模型/未分类';
            type = '3D模型';
            tags = '我的模型';
          } else if (TEMPLATE_SUFFIXES.includes(suffix)) {
            directory = '/3D/面板/未分类';
            type = 'L3DM';
            tags = '我的面板';
          } else if (IMAGE_SUFFIXES.includes(suffix)) {
            directory = '/3D/图片/未分类';
            type = '图片';
            tags = '我的图片';
          } else if (HDR_SUFFIXES.includes(suffix)) {
            directory = '/3D/HDR/未分类';
            type = 'HDR';
            tags = '我的HDR';
          }
          if (directory) {
            const res = await uploadFile({
              file: newFile,
              directory,
              type,
              tags,
            });
            if (res) {
              newUrlMap[url] = res.url;
              // uploaded++;
              return;
            }
          }
        }
        // failed++;
      } catch (e) {
        // failed++;
      } finally {
      }
    })
  );
  const newData = meta3dReplaceUrl(data, newUrlMap);

  //上传
  const projectData = meta3dCrypto(JSON.stringify(newData));
  const res: any = await axios.post('/api/data/3d/add', {
    data: {
      data: projectData,
      scenes: [],
      updatedCount: 1,
    },
    image: 'xxx',
    name: newData.name,
  });
  if (res) {
    return res.id;
  } else {
    return '';
  }
}
export function s16() {
  let chars = [
    '0',
    '1',
    '2',
    '3',
    '4',
    '5',
    '6',
    '7',
    '8',
    '9',
    'A',
    'B',
    'C',
    'D',
    'E',
    'F',
    'G',
    'H',
    'I',
    'J',
    'K',
    'L',
    'M',
    'N',
    'O',
    'P',
    'Q',
    'R',
    'S',
    'T',
    'U',
    'V',
    'W',
    'X',
    'Y',
    'Z',
    'a',
    'b',
    'c',
    'd',
    'e',
    'f',
    'g',
    'h',
    'i',
    'j',
    'k',
    'l',
    'm',
    'n',
    'o',
    'p',
    'q',
    'r',
    's',
    't',
    'u',
    'v',
    'w',
    'x',
    'y',
    'z',
  ];
  let strs = '';
  for (let i = 0; i < 16; i++) {
    let id = Math.ceil(Math.random() * 61);
    strs += chars[id];
  }
  return strs;
}

const a = (word: string, k: string, i: string) => {
  let srcs = CryptoJS.enc.Utf8.parse(word);
  let encrypted = CryptoJS.AES.encrypt(srcs, CryptoJS.enc.Utf8.parse(k), {
    iv: CryptoJS.enc.Utf8.parse(i),
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7,
  });
  return encrypted.ciphertext.toString().toUpperCase();
};

const c = (word: string): string => {
  const k = s16().toUpperCase();
  const i = s16().toUpperCase();
  const d = a(word, k, i);
  return k + d + i;
};

const meta3dCrypto = (data: string) => {
  return c(data);
};

export const getUrlWithoutSearch = (url: string) => {
  return url.split('?')[0];
};

export function filename(str: string, withSuffix = false) {
  const paths = getUrlWithoutSearch(str).split('/');
  const name = paths[paths.length - 1];
  if (withSuffix) {
    return name;
  }
  const i = name.lastIndexOf('.');
  if (i === -1) {
    return name;
  }
  return name.substring(0, i);
}

export const uploadFile = async (info: {
  file: File;
  directory: string;
  type: UploadFileType;
  tags?: string;
  remarks?: string;
  name?: string;
}) => {
  const { file, directory, type, tags = '', remarks = '', name = '' } = info;
  const formData = new FormData();
  formData.append('file', file);
  formData.append('directory', directory);
  formData.append('type', type);
  formData.append('tags', tags);
  formData.append('remarks', remarks);
  formData.append('shared', 'true');
  if (name) {
    formData.append('name', name);
  }
  const res = (await axios.post(
    '/api/file/upload',
    formData
  )) as ResponseResult<FileData>;
  if (res.error) {
  }
  return res.data;
};

export type UploadFileType =
  | '文档'
  | '图片'
  | '3D模型'
  | '视频'
  | '音乐'
  | '其他'
  | string;

export interface ResponseResult<T> {
  success: boolean;
  data?: T;
  error?: string;
  detail?: string;
}

export interface FileData {
  createdAt: string;
  directory: string;
  fullname: string;
  id: string;
  name: string;
  ownerId: string;
  ownerName: string;
  shared: boolean;
  size: number;
  tags: string[];
  type: UploadFileType;
  updatedAt: string;
  url: string;
}
const b = (word: string, k: string, i: string) => {
  let encryptedHexStr = CryptoJS.enc.Hex.parse(word);
  let srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr);
  let decrypt = CryptoJS.AES.decrypt(srcs, CryptoJS.enc.Utf8.parse(k), {
    iv: CryptoJS.enc.Utf8.parse(i),
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7,
  });
  let decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);
  return decryptedStr.toString();
};

const d = (word: string): string => {
  const k = word.substring(0, 16);
  const i = word.substring(word.length - 16);
  return b(word.substring(16, word.length - 16), k, i);
};

const parseData = (data: Partial<any> | string): Partial<any> => {
  if (typeof data === 'string') {
    data = data.startsWith('{') ? data : d(data);
    return JSON.parse(data);
  }
  return data;
};

export function filepath(str: string) {
  const paths = str.split('/');
  paths[paths.length - 1] = '';
  return paths.join('/');
}

const meta3dReplaceUrl = (
  data: any | string,
  urlMap: { [url: string]: string }
) => {
  const _data = parseData(data);
  const { scenes } = _data;
  const transUrl = (url: string): string => {
    if (url.startsWith('data:')) {
      const str = url.substring(5);
      const [image, ...arr] = str.split('#').reverse();
      url = arr.reverse().join('#');
      const newUrl = transUrl(url);
      return 'data:' + newUrl + '#' + image;
    }
    if (url in urlMap) {
      return urlMap[url];
    }
    return url;
  };
  const loadFile = (data: any, glbMap: any) => {
    if (data.__glbUUID && glbMap) {
      const glbInfo = glbMap[data.__glbUUID];
      if (glbInfo) {
        const { url, name } = glbInfo;
        const fullUrl = url + name;
        if (fullUrl in urlMap) {
          glbInfo.url = filepath(urlMap[fullUrl]);
          glbInfo.name = filename(urlMap[fullUrl], true);
        }
      }
    }
    const urlProps = [
      'url',
      'imageSource',
      'skyboxUrl',
      'hdrUrl',
      'colorGradingTexture',
    ];
    for (const urlProp of urlProps) {
      if (urlProp in data === false) {
        continue;
      }
      const url = data[urlProp] || '';
      data[urlProp] = transUrl(url);
    }
    if (data.images?.length) {
      for (const image of data.images) {
        const { source } = image;
        image.source = transUrl(source);
      }
    }
  };

  for (const sceneData of scenes) {
    if (!sceneData) {
      continue;
    }
    const { nodes = [], scene = {}, glbMap = {}, textures = {} } = sceneData;
    for (const node of [
      scene,
      ...nodes,
      ...Object.keys(textures).map((id) => textures[id]),
    ]) {
      loadFile(node, glbMap);
    }
  }

  return _data;
};
