import { Service } from './Service';
import { PorjectSchema, IFileSchema, TemplateDescription } from './types';
import { IFile } from './models';
import { Engine } from './Engine';

const PREFIX = 'VTJ_';

function save(key: string, data: any) {
  localStorage.setItem(PREFIX + key, JSON.stringify(data));
}

function get<T>(key: string) {
  return JSON.parse(localStorage.getItem(PREFIX + key) || 'null') as T;
}

function clear(key: string) {
  localStorage.removeItem(PREFIX + key);
}

function append(key: string, name: string, item: any) {
  const data: any = get(key) || {};
  let items = data[name] || [];
  items.push(item);
  data[name] = items;
  save(key, data);
}

function update(key: string, name: string, item: any) {
  const data: any = get(key) || {};
  const items = data[name];
  if (!items) {
    return;
  }
  const index = items.findIndex((i: any) => i.id === item.id);
  if (index > -1) {
    items.splice(index, 1, item);
  }
  save(key, data);
}

function remove(key: string, name: string, id: string) {
  const data: any = get(key) || {};
  const items = data[name];
  if (!items) {
    return;
  }
  const index = items.findIndex((i: any) => i.id === id);
  if (index > -1) {
    items.splice(index, 1);
  }
  save(key, data);
}

export class StorageService extends Service {
  public async loadProject(
    engine: Engine,
    project?: PorjectSchema
  ): Promise<PorjectSchema> {
    const existProject = get('project');
    if (!existProject) {
      const newProject = {
        name: 'vtj-project',
        title: '未命名项目',
        files: [],
        ...project
      };
      save('project', newProject);
      return newProject;
    }
    return existProject as PorjectSchema;
  }

  public async createFile(engine: Engine, file: IFile): Promise<boolean> {
    const summary = file.toSummarySchema();
    const schema = file.toSchema();
    append('project', 'files', summary);
    save(`file_${schema.id}`, schema);
    return true;
  }

  public async getFile(engine: Engine, id: string): Promise<IFileSchema> {
    return get<IFileSchema>(`file_${id}`);
  }

  public async updateFile(engine: Engine, file: IFile): Promise<boolean> {
    const summary = file.toSummarySchema();
    const schema = file.toSchema();
    update('project', 'files', summary);
    save(`file_${schema.id}`, schema);
    return true;
  }

  public async removeFile(engine: Engine, file: IFile): Promise<boolean> {
    clear(`file_${file.id}`);
    remove('project', 'files', file.id);
    const histories = await this.getFileHistories(engine, file.id);
    for (let id of histories) {
      clear(`history_${file.id}_${id}`);
    }
    clear(`history_${file.id}`);
    return true;
  }

  public async updateProject(
    engine: Engine,
    project: PorjectSchema
  ): Promise<PorjectSchema> {
    const existProject = get('project') || {};
    const newProject = Object.assign(existProject as PorjectSchema, project);
    save('project', newProject);
    return newProject;
  }

  public async saveHistory(engine: Engine, file: IFile): Promise<boolean> {
    const schema = file.toSchema();
    const fileId = file.id;
    const histories: string[] = get(`history_${fileId}`) || [];
    const newId = Date.now().toString();
    const max = 30;
    histories.unshift(newId);
    save(`history_${fileId}`, histories);
    save(`history_${fileId}_${newId}`, schema);
    if (histories.length > max) {
      const oldHistories = histories.splice(max);
      save(`history_${fileId}`, histories);
      for (let item of oldHistories) {
        clear(`history_${fileId}_${item}`);
      }
    }
    return true;
  }

  public async removeHistory(
    engine: Engine,
    file: IFile,
    id: string
  ): Promise<boolean> {
    const fileId = file.id;
    let histories: string[] = get(`history_${fileId}`) || [];
    histories = histories.filter((n) => n !== id);
    save(`history_${fileId}`, histories);
    clear(`history_${fileId}_${id}`);
    return true;
  }

  public async getFileHistories(engine: Engine, id: string): Promise<string[]> {
    return get(`history_${id}`) || [];
  }

  public async getHistory(
    engine: Engine,
    fileId: string,
    id: string
  ): Promise<IFileSchema> {
    return get<IFileSchema>(`history_${fileId}_${id}`);
  }

  public async removeTemplate(engine: Engine, name: string): Promise<boolean> {
    const templates = get<TemplateDescription[]>('templates') || [];
    const index = templates.findIndex((n) => n.name === name);
    if (index > -1) {
      templates.splice(index, 1);
    }
    save('templates', templates);
    return true;
  }

  public async addTemplate(
    engine: Engine,
    template: TemplateDescription
  ): Promise<boolean> {
    const templates = get<TemplateDescription[]>('templates') || [];
    templates.push(template);
    save('templates', templates);
    return true;
  }

  public async getTemplates(): Promise<TemplateDescription[]> {
    return get<TemplateDescription[]>('templates') || [];
  }
}
