import { Command, FileDecoration, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri } from 'vscode';

/**
 * Class which represents a resource within the repository, containing its URI and Pijul state
 */
export class Resource implements SourceControlResourceState {
  /**
   * Create a new resource
   * @param _resourceUri The URI of the resource
   * @param status The Pijul status of the resource
   */
  constructor (
    private readonly _resourceUri: Uri,
    private readonly status: ResourceStatus
  ) { }

  /**
   * The URI of the resource
   */
  get resourceUri (): Uri {
    // TODO: Handle moves?
    return this._resourceUri;
  }

  /**
   * The command which will be run when the resource is clicked on
   */
  get command (): Command {
    return {
      command: 'vscode.open',
      title: 'Open',
      arguments: [this.resourceUri]
    };
  }

  /**
   * The tooltip briefly explaining the state of the resource
   */
  private get tooltip (): string {
    switch (this.status) {
      case ResourceStatus.Edit: return 'Edit';
      case ResourceStatus.Replacement: return 'Replacement';
      case ResourceStatus.FileAdd: return 'Added';
      case ResourceStatus.FileMove: return 'Moved';
      case ResourceStatus.FileDel: return 'Deleted';
      case ResourceStatus.FileUndel: return 'Undeleted';
      case ResourceStatus.SolveNameConflict: return 'Solve Name Conflict';
      case ResourceStatus.SolveOrderConflict: return 'Solve Order Conflict';
      case ResourceStatus.UnsolveNameConflict: return 'Unsolve Name Conflict';
      case ResourceStatus.UnsolveOrderConflict: return 'Unsolve Order Conflict';
      case ResourceStatus.ResurrectZombies: return 'Resurrect Zombies';
      case ResourceStatus.Untracked: return 'Untracked';
      // I don't know why typescript doesn't complain about this one
      default: return '';
    }
  }

  /**
   * Boolean indicating if the filename should be rendered with strikethrough
   */
  private get strikeThrough (): boolean {
    return this.status === ResourceStatus.FileDel;
  }

  /**
   * Boolean indicating if the filename should be faded
   */
  private get faded (): boolean {
    return this.status === ResourceStatus.Untracked;
  }

  /**
   * Accessor for the decoration applied to the resource in the source control menu
   */
  get decorations (): SourceControlResourceDecorations {
    const tooltip = this.tooltip;
    const strikeThrough = this.strikeThrough;
    const faded = this.faded;
    return { strikeThrough, faded, tooltip };
  }

  /**
   * The badge, a short string indicating the state of the resource
   */
  get badge (): string {
    switch (this.status) {
      case ResourceStatus.FileMove:
        return 'MV';
      case ResourceStatus.FileDel:
        return 'D';
      case ResourceStatus.FileUndel:
        return 'UD';
      case ResourceStatus.FileAdd:
        return 'A';
      case ResourceStatus.SolveNameConflict:
      case ResourceStatus.SolveOrderConflict:
        return 'SC';
      case ResourceStatus.UnsolveNameConflict:
      case ResourceStatus.UnsolveOrderConflict:
        return 'UC';
      case ResourceStatus.Edit:
        return 'M';
      case ResourceStatus.Replacement:
        return 'R';
      case ResourceStatus.ResurrectZombies:
        return 'RZ';
      case ResourceStatus.Untracked:
        return '';
      default:
        // This should never happen so typescript won't accept it without error, but
        // I'm leaving it in for safety.
        // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
        throw new Error(`Unknown pijul status: ${this.status}`);
    }
  }

  /**
   * The theme colour that should be used for the badge
   */
  get colour (): ThemeColor {
    switch (this.status) {
      case ResourceStatus.Replacement:
        return new ThemeColor('pijulDecoration.replacementForeground');
      case ResourceStatus.Edit:
        return new ThemeColor('pijulDecoration.editForeground');
      case ResourceStatus.FileDel:
      case ResourceStatus.FileUndel:
        return new ThemeColor('pijulDecoration.deletedResourceForeground');
      case ResourceStatus.FileAdd:
        return new ThemeColor('pijulDecoration.addedForeground');
      case ResourceStatus.FileMove:
      case ResourceStatus.Untracked:
        return new ThemeColor('pijulDecoration.untrackedForeground');
      case ResourceStatus.SolveNameConflict:
      case ResourceStatus.SolveOrderConflict:
      case ResourceStatus.UnsolveNameConflict:
      case ResourceStatus.UnsolveOrderConflict:
      case ResourceStatus.ResurrectZombies:
        return new ThemeColor('pijulDecoration.conflictForeground');
      default:
        // This should never happen so typescript won't accept it without error, but
        // I'm leaving it in for safety.
        // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
        throw new Error(`Unknown pijul status: ${this.status}`);
    }
  }

  /**
   * Accessor for the file decoration that should be applied to this resource
   */
  get resourceDecoration (): FileDecoration {
    const decorations = new FileDecoration(this.badge, this.tooltip, this.colour);
    decorations.propagate = this.status !== ResourceStatus.FileDel;
    return decorations;
  }
}

/**
 * Enum corresponding to the various Pijul file states. Using a string enum
 * allows for direct casting of the operation string in a pijul JSON diff.
 */
export enum ResourceStatus {
  FileMove = 'file move',
  FileDel = 'file del',
  FileUndel = 'file undel',
  SolveNameConflict = 'solve name conflict',
  UnsolveNameConflict = 'unsolve name conflict',
  FileAdd = 'file add',
  Edit = 'edit',
  Replacement = 'replacement',
  SolveOrderConflict = 'solve order conflict',
  UnsolveOrderConflict = 'unsolve order conflict',
  ResurrectZombies = 'resurrect zombies',
  Untracked = 'untracked'
}

/**
 * Interface which represents a groups of resources that is tracked by
 * the Pijul source control manager.
 */
export interface PijulResourceGroup extends SourceControlResourceGroup {
  resourceStates: Resource[]
}