// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/* eslint-disable rulesdir/no_underscored_properties */

import * as Common from '../common/common.js';
import * as i18n from '../i18n/i18n.js';
import * as Persistence from '../persistence/persistence.js';
import * as Platform from '../platform/platform.js';
import * as TextUtils from '../text_utils/text_utils.js';  // eslint-disable-line no-unused-vars
import * as Workspace from '../workspace/workspace.js';    // eslint-disable-line no-unused-vars

export const UIStrings = {
  /**
  * @description Default name of a new recording
  * @example {1} nextId
  */
  defaultRecordingName: 'Recording #{nextId}',
  /**
  * @description Text to show something is linked to another
  * @example {example.url} PH1
  */
  linkedToS: 'Linked to {PH1}',
};
const str_ = i18n.i18n.registerUIStrings('recorder/RecordingFileSystem.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);

function escapeRecordingName(name: string): string {
  return escape(name);
}

function unescapeRecordingName(name: string): string {
  return unescape(name);
}

export class RecordingFileSystem extends Persistence.PlatformFileSystem.PlatformFileSystem {
  _lastRecordingIdentifierSetting: Common.Settings.Setting<number>;
  _recordingsSetting: Common.Settings.Setting<Recording[]>;

  constructor() {
    super('recording://', 'recordings');
    this._lastRecordingIdentifierSetting =
        Common.Settings.Settings.instance().createSetting('recorder_lastIdentifier', 0);
    this._recordingsSetting = Common.Settings.Settings.instance().createSetting('recorder_recordings', []);
  }

  initialFilePaths(): string[] {
    const savedRecordings: Recording[] = this._recordingsSetting.get();
    return savedRecordings.map(recording => escapeRecordingName(recording.name));
  }

  async createFile(_path: string, _name: string|null): Promise<string|null> {
    const nextId = this._lastRecordingIdentifierSetting.get() + 1;
    this._lastRecordingIdentifierSetting.set(nextId);

    const recordingName = i18nString(UIStrings.defaultRecordingName, {nextId: nextId});
    const recordings = this._recordingsSetting.get();
    recordings.push({name: recordingName, content: '{"steps": []}'});
    this._recordingsSetting.set(recordings);

    return escapeRecordingName(recordingName);
  }

  async deleteFile(path: string): Promise<boolean> {
    const name = unescapeRecordingName(path.substring(1));
    const allRecordings: Recording[] = this._recordingsSetting.get();
    const recordings = allRecordings.filter(recording => recording.name !== name);
    if (allRecordings.length !== recordings.length) {
      this._recordingsSetting.set(recordings);
      return true;
    }
    return false;
  }

  async requestFileContent(path: string): Promise<TextUtils.ContentProvider.DeferredContent> {
    const name = unescapeRecordingName(path.substring(1));
    const recordings: Recording[] = this._recordingsSetting.get();
    const recording = recordings.find(recording => recording.name === name);
    if (recording) {
      return {content: recording.content, isEncoded: false};
    }
    return {content: null, isEncoded: false, error: `A recording with name '${name}' was not found`};
  }

  async setFileContent(path: string, content: string, _isBase64: boolean): Promise<boolean> {
    const name = unescapeRecordingName(path.substring(1));
    const recordings: Recording[] = this._recordingsSetting.get();
    const recording = recordings.find(recording => recording.name === name);
    if (recording) {
      recording.content = content;
      this._recordingsSetting.set(recordings);
      return true;
    }
    return false;
  }

  renameFile(path: string, newName: string, callback: (arg0: boolean, arg1?: string|undefined) => void): void {
    const name = unescapeRecordingName(path.substring(1));
    const recordings: Recording[] = this._recordingsSetting.get();
    const recording = recordings.find(recording => recording.name === name);
    newName = newName.trim();
    if (!recording || newName.length === 0 || recordings.find(recording => recording.name === newName)) {
      callback(false);
      return;
    }
    recording.name = newName;
    this._recordingsSetting.set(recordings);
    callback(true, newName);
  }

  async searchInPath(query: string, _progress: Common.Progress.Progress): Promise<string[]> {
    const re = new RegExp(Platform.StringUtilities.escapeForRegExp(query), 'i');
    const allRecordings: Recording[] = this._recordingsSetting.get();
    const matchedRecordings = allRecordings.filter(recording => recording.content.match(re));
    return matchedRecordings.map(recording => `recording:///${escapeRecordingName(recording.name)}`);
  }

  mimeFromPath(_path: string): string {
    return 'text/javascript';
  }

  contentType(_path: string): Common.ResourceType.ResourceType {
    return Common.ResourceType.resourceTypes.Script;
  }

  tooltipForURL(url: string): string {
    return i18nString(UIStrings.linkedToS, {PH1: unescapeRecordingName(url.substring(this.path().length))});
  }

  supportsAutomapping(): boolean {
    return true;
  }
}

export function isRecordingUISourceCode(uiSourceCode: Workspace.UISourceCode.UISourceCode): boolean {
  return uiSourceCode.url().startsWith('recording://');
}

export function isRecordingProject(project: Workspace.Workspace.Project): boolean {
  return project.type() === Workspace.Workspace.projectTypes.FileSystem &&
      Persistence.FileSystemWorkspaceBinding.FileSystemWorkspaceBinding.fileSystemType(project) === 'recordings';
}

export function findRecordingsProject(): Workspace.Workspace.Project {
  const workspace = Workspace.Workspace.WorkspaceImpl.instance();
  const projects = workspace.projectsForType(Workspace.Workspace.projectTypes.FileSystem);
  const project = projects.find(project => {
    const type = Persistence.FileSystemWorkspaceBinding.FileSystemWorkspaceBinding.fileSystemType(project);
    return type === 'recordings';
  });
  if (!project) {
    throw new Error('Unable to find workspace project for the recordings file system.');
  }
  return project;
}
export interface Recording {
  name: string;
  content: string;
}
