import { Service, Inject } from 'typedi';
import winston from 'winston';
import path, { join } from 'path';
import SockService from './sock';
import CronService from './cron';
import ScheduleService, { TaskCallbacks } from './schedule';
import config from '../config';
import { TASK_COMMAND } from '../config/const';
import { getFileContentByName, getPid, killTask, rmPath } from '../config/util';
import taskLimit from '../shared/pLimit';

@Service()
export default class ScriptService {
  constructor(
    @Inject('logger') private logger: winston.Logger,
    private sockService: SockService,
    private cronService: CronService,
    private scheduleService: ScheduleService,
  ) {}

  private taskCallbacks(filePath: string): TaskCallbacks {
    return {
      onEnd: async (cp, endTime, diff) => {
        await rmPath(filePath);
      },
      onError: async (message: string) => {
        this.sockService.sendMessage({
          type: 'manuallyRunScript',
          message,
        });
      },
      onLog: async (message: string) => {
        this.sockService.sendMessage({
          type: 'manuallyRunScript',
          message,
        });
      },
    };
  }

  public async runScript(filePath: string) {
    const relativePath = path.relative(config.scriptPath, filePath);
    const command = `${TASK_COMMAND} ${relativePath} now`;
    const pid = await this.scheduleService.runTask(
      `real_time=true ${command}`,
      this.taskCallbacks(filePath),
      { command, id: relativePath.replace(/ /g, '-'), runOrigin: 'script' },
      'start',
    );

    return { code: 200, data: pid };
  }

  public async stopScript(filePath: string, pid: number) {
    if (!pid) {
      const relativePath = path.relative(config.scriptPath, filePath);
      taskLimit.removeQueuedCron(relativePath.replace(/ /g, '-'));
      pid = (await getPid(`${TASK_COMMAND} ${relativePath} now`)) as number;
    }
    try {
      await killTask(pid);
    } catch (error) {}

    return { code: 200 };
  }

  public checkFilePath(filePath: string, fileName: string) {
    const finalPath = path.resolve(config.scriptPath, filePath, fileName);
    return finalPath.startsWith(config.scriptPath) ? finalPath : '';
  }

  public async getFile(filePath: string, fileName: string) {
    const finalPath = this.checkFilePath(filePath, fileName);

    if (!finalPath) {
      return '';
    }

    const content = await getFileContentByName(finalPath);
    return content;
  }
}