/**
 * 更新发布工具
 * 
 * Copyright © 2025 imengyu.top imengyu-update-server
 */

import { program } from 'commander';
import { password, input, confirm, select } from '@inquirer/prompts';
import { writeFile, readFile } from 'node:fs/promises';
import { postAppUpdate, postWebUpdate } from './postUpdate.mjs';
import Table from 'cli-table3';
import md5 from 'md5';
import axios from 'axios';
import path from 'path';
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

//基础配置
//========================================

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const constant = {
  ServerUrl: 'http://update-server1.imengyu.top/',
  TokenSave: path.resolve(__dirname, './_token.json'),
};
let currentData = {
  token: '',
  identifier: '',
};

readFile(constant.TokenSave).then((res) => {
  currentData = JSON.parse(res);
  if (!currentData.identifier) 
    currentData.identifier = `commandClient${Math.floor(Math.random() * 1000)}`;
  start();
}).catch(() => {
  start();
})

const axiosInstance = axios.create({
  baseURL: constant.ServerUrl,
  timeoutErrorMessage: '请求超时，请检查网络连接',
  responseType: 'json',
  withCredentials: false,
  validateStatus: () => true,
});

axiosInstance.interceptors.request.use((value) => {
  value.headers['authorization'] = JSON.stringify({
    auth: currentData?.token?.authName,
    validity: currentData?.token?.authKey,
    nonce: "aaaaaaaaaa",
    identifier: currentData.identifier,
    key: 'abc123',
  });  
  value.url = value.url + (value.url.includes('?') ? '&' : '?' ) + `identifier=${currentData.identifier}`
  return value;
});
axiosInstance.interceptors.response.use((value) => {
  if (value.data.success) 
    return value.data;
  else
    return Promise.reject(value.data);
});

function getErrorMessage(e) {
  return e instanceof Error ? e.message : (typeof e === 'object' ? e : ('' + e));
}

//登录相关
//========================================

async function checkLogged() {
  try {
    await axiosInstance.get('/auth');
    console.log('已登录');
  } catch (e) {
    console.error('获取状态失败：', getErrorMessage(e));
  }
}
async function login(user) {
  try {
    const pass = await password({ message: '输入密码' });
    const res = await axiosInstance.post('/auth?rember=true', {
      method: 'key',
      key: `${user}@${md5(pass)}`,
    })
    currentData.token = {
      authName: res.data.authName,
      authKey: res.data.authKey,
    };
    writeFile(constant.TokenSave, JSON.stringify(currentData));
    console.log('登录成功');
  } catch (e) {
    console.error('登录失败', getErrorMessage(e));
  }
}
async function logout() {
  currentToken = '';
  writeFile(constant.TokenSave, JSON.stringify(currentData));
  try {
    await axiosInstance.delete('/auth');
    console.log('退出登录成功');
  } catch(e) {
    console.error('退出登录失败', getErrorMessage(e));
  }
}

//版本相关
//========================================

async function viewVersion(type) {
  if (type === 'all' || !type) {
    const data = await axiosInstance.get('/version/list');
    const table = new Table({
      head: ['ID', '版本'], 
      colWidths: [10, 20 ]
    });

    data.data.forEach((d) => {
      table.push([ d.id, d.version ]);
    })
    console.log(table.toString());
  } else {
    let data = null;
    try {
      if (Number.isNaN(Number(type)))
        data = await axiosInstance.get('/version/get-by-name?name=' + type);
      else
        data = await axiosInstance.get('/version/' + type);
    }
    catch (e) {
      console.error('Failed to load version info', e);
      return;
    }
    const table = new Table({
      head: ['key', 'data'], 
      colWidths: [30, 60]
    });
    table.push(
      [ 'ID', data.data.id ],
      [ '状态', stateConstant[data.data.status] ],
      [ '版本号', data.data.version ],
      [ '创建时间', new Date(data.data.createAt).toString() ],
      [ '设置', data.data.config ],
      [ '激活的Web更新ID', data.data.webUpdateId ],
      [ '激活的App更新ID', data.data.appUpdateId ],
      [ '激活的下一个App更新ID', data.data.appUpdateNextId ],
    );
    console.log(table.toString());
  }
}
async function getVersion(action, type) {
  switch(action) {
    case 'view':
      await viewVersion(type);
      break;
    case 'new': {
      const version = await input({ message: 'Enter version name, like (1.0.0)' });
      try {
        await axiosInstance.post('/version', {
          version: version,
          status: 1,
          config: "{}",
        });
        console.log('Add version success');
      } catch (e) {
        console.error('Failed to add version', e);
      }
      break;
    }
    case 'delete': {
      const versionId = type ? type : await input({ message: '输入版本ID' });
      if (!await confirm({ message: `确定删除版本 ${versionId}?`, default: false }))
        return;
      if (!await confirm({ message: '确认删除版本？此操作会删除所属版本的所有更新项目、存储等，无法恢复，是否确定删除？', default: false }))
        return;
      try {
        await axiosInstance.delete('/version/' + versionId);
        console.log('删除版本成功');
      } catch (e) {
        console.error('删除版本失败', e);
      }
      break;
    }
    case 'set-state': {
      const versionId = type ? type : await input({ message: '输入版本ID' });
      const state = await select({
        message: '设置状态',
        choices: [
          {
            name: 'NotEnable',
            value: 0,
          },
          {
            name: 'Normal',
            value: 1,
          },
          {
            name: 'Deprecated',
            value: 2,
          },
        ],
      });

      try {
        await axiosInstance.put('/version/' + versionId, {
          status: state
        });
        console.log('设置状态成功');
      } catch (e) {
        console.error('设置状态失败', e);
      }
      break;
    }
    case 'set-config': {
      const versionId = type ? type : await input({ message: '输入版本ID' });
      const config = await input({ message: '输入配置Json' });
      try {
        await axiosInstance.put('/version/' + versionId, {
          config: config,
        });
        console.log('设置配置成功');
      } catch (e) {
        console.error('设置配置失败', e);
      }
      break;
    }
    case 'set-active-app-update': {
      const versionId = await input({ message: '输入版本ID' });
      const updateId = await input({ message: '输入更新ID' });
      const isNext = await confirm({ message: 'Set as next active?', default: false });

      try {
        await axiosInstance.post('/update/active/app', { versionId, updateId, isNext });
        console.log('成功');
      } catch (e) {
        console.error('失败', e);
      }
      break;
    }
    case 'set-active-web-update': {
      const versionId = await input({ message: '输入版本ID' });
      const updateId = await input({ message: '输入更新ID' });

      try {
        await axiosInstance.post('/update/active/web', { versionId, updateId });
        console.log('成功');
      } catch (e) {
        console.error('失败', e);
      }
      break;
    }
    default:
      console.error('未知参数', action);
      break;
  }
}

//选择方法
//========================================

export async function selectVersion(requireString = false, defaultVersionId = null) {
  const data = (await axiosInstance.get('/version/list')).data;
  if (data.length === 0) {
    console.error('没有版本');
    return;
  }
  const resultId = (await select({
    choices: data.map(p => ({
      value: p.id,
      name: p.version,
    })),
    default: defaultVersionId,
    message: '选择一个版本',
  }));
  if (requireString) {
    return data.find(p => p.id === resultId).version
  }
  return resultId;
}
export async function selectUpdate() {
  const versionId = await selectVersion();
  const data = (await axiosInstance.get('/version/update?search=' + JSON.stringify({ versionId }))).data;
  if (data.length === 0) {
    console.error('没有更新');
    return;
  }
  const resultId = (await select({
    choices: data.map(p => ({
      value: p.id,
      name: p.version,
    })),
    default: defaultVersionId,
    message: '选择一个更新',
  }));
  return resultId;
}

//更新相关
//========================================

const stateConstant = [ 'Deleted', 'Normal', 'Deprecated' ];
const typeConstant = [ 'Unknow', 'Web', 'app' ];
const storageTypeConstant = [ 'Unknow', 'LocalStorage', 'AliOSS' ];

async function postUpdate(type, options) {
  switch (type) {
    case 'web': {
      await postWebUpdate(axiosInstance, options);
      break;
    }
    case 'app': {
      await postAppUpdate(axiosInstance, options);
      break;
    }
    default:
      console.error('Unknow type', type);
      break;
  }
}
async function deprecateOrDeleteUpdate(updateId) {
  if (!updateId)
    updateId = await selectUpdate();

  const { currentUpdateInfo, currentVersionInfo } = await viewUpdate(updateId);

  const deprecate = (await select({
    choices: [
      {
        name: 'Deprecate',
        value: 0,
      },
      {
        name: 'Delete',
        value: 1,
      },
    ],
    message: '删除或弃用?',
  })) === 0;

  if (deprecate && currentUpdateInfo.type !== 1) {
    console.log('只有Web更新允许弃用');
    return;
  }
  if (currentUpdateInfo.status === 0) {
    console.log(`当前状态 ${stateConstant[currentUpdateInfo.status]} 无法弃用`);
    return;
  }

  if (deprecate) {
    if (!await confirm({ message: `确定弃用当前版本 ${currentUpdateInfo.versionCode} ？弃用会删除存储文件以及备份。此操作无法恢复！`, default: false }))
      return;
  } else {
    if (!await confirm({ message: `确定删除当前版本 ${currentUpdateInfo.versionCode} ？此操作无法恢复！`, default: false }))
      return;
  }

  try {
    await axiosInstance.post(`/update/${deprecate ? 'deprecate' : 'delete'}`, { updateId });
    console.log(`${deprecate ? '弃用' : '删除'} 成功`);
  } catch (e) {
    console.error(`操作失败`, e);
  }
}
async function viewUpdate(updateId) {
  
  let currentUpdateInfo = null
  let currentVersionInfo = null

  try {
    currentUpdateInfo = (await axiosInstance.get('/update/' + updateId)).data;
  } catch (e) {
    console.error('加载更新信息失败', updateId);
  }
  try {
    currentVersionInfo = (await axiosInstance.get('/version/' + currentUpdateInfo.versionId)).data;
  } catch (e) {
    console.error('加载版本信息失败', currentUpdateInfo.versionId);
  }

  const table = new Table({
    head: ['key', 'data'], 
    colWidths: [20, 40]
  });
  table.push(
    [ 'ID', currentUpdateInfo.id ],
    [ '所属应用', currentUpdateInfo.name ],
    [ '更新信息', currentUpdateInfo.updateInfo ],
    [ '版本号', currentUpdateInfo.versionCode ],
    [ '创建时间', new Date(currentUpdateInfo.createAt).toString() ],
    [ '类型', typeConstant[currentUpdateInfo.type] ],
    [ '状态', stateConstant[currentUpdateInfo.status] ],
    [ '强制更新', currentUpdateInfo.force ],
    [ '公共访问路径', currentUpdateInfo.publicUrl ],
    [ '存储类型', storageTypeConstant[currentUpdateInfo.storageType] ],
    [ '存储路径', currentUpdateInfo.storagePath ],
  );

  console.log(table.toString());

  if (currentUpdateInfo.activeWebVersionName) {
    table.push(
      [ '使用中的Web版本', currentUpdateInfo.activeWebVersionName ],
    );
  }
  if (currentUpdateInfo.activeAppVersionName) {
    table.push(
      [ '使用中的App版本', currentUpdateInfo.activeAppVersionName ],
    );
  }

  return {
    currentUpdateInfo,
    currentVersionInfo,
  }
}
async function getUpdate(action, type, all, options) {
  const typeNotANumber = isNaN(new Number(type)); 

  if (action === 'view' && (!type || typeNotANumber)) {

    let hasSerch = false;
    const search = {};
    const sort = {
      field: "createAt",
      order: "descend"
    }
    if (typeNotANumber && type !== 'all') {
      hasSerch = true;
      search.version = type;
    }

    const data = await axiosInstance.get('/update/list?full=true' + (hasSerch ? ('&search=' + JSON.stringify(search)) : '') + '&sort=' + JSON.stringify(sort));
    const table = new Table({
      head: ['ID', '版本', '版本号', '类型', '状态'], 
      colWidths: [10, 10, 20, 10, 15 ]
    });

    if (type !== 'all' && all !== 'all' && data.data.length > 10) {
      data.data = data.data.slice(0, 10);
      console.log('filter!', all);
    }

    data.data.forEach((d) => {
      table.push([ 
        d.id, 
        (d.activeAppVersionName ? `${d.activeAppVersionName} (App)` : (
          d.activeWebVersionName? `${d.activeWebVersionName} (Web)` : '无'
        )),  
        d.versionCode, typeConstant[d.type], stateConstant[d.status] 
      ]);
    })
    
    console.log(table.toString());
  } else {
    switch (action) {
      case 'post': {
        await postUpdate(type, options);
        break;
      }
      case 'delete': {
        await deprecateOrDeleteUpdate(type);
        break;
      }
      case 'view': {
        viewUpdate(type);
        break;
      }
      default: 
        console.error('Unknow action', action);
        break;
    }
  }
}
async function testVersion(version) {
  try {
    const data = await axiosInstance.get('/update-get-info?version=' + version);
    console.log('版本信息', data.data);
  } catch(e) {
    console.log('获取失败', e);
  }
}

//程序入口
//============================================

program
  .command('login <user>')
  .description('登录')
  .action(login);
program
  .command('logstate')
  .description('检查登录状态')
  .action(checkLogged);
program
  .command('logout')
  .description('退出登录')
  .action(logout);

program
  .command('test <version>')
  .description('测试更新入口')
  .action(testVersion);

program
  .command('version <action> [type]')
  .description('查看版本信息/发布版本/删除版本/设置版本, action 可选 view/new/delete/set-web-update/set-app-update/set-next-app-update')
  .action(getVersion);
program
  .command('update <action> [type] [all]')
  .description('查看更新信息/发布更新, action 可选 view/post, view type 可选 id/all; post type 可选 web/app')
  .option('--skip', '跳过构建')
  .option('--ndelete', '不删除构建文件')
  .action(getUpdate);

function start() {
  program.parse(process.argv);
}

process.on('unhandledRejection', (reason, p) => {
  console.error('Promise: ', p, 'Reason: ', reason)
})