import { Event, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
import { PijulChange, PijulChannel, Repository } from '../pijul';

/**
 * The Channels provider class is a Treedata provider which provides
 * TreeData for the `pijul.views.channels` TreeView
 */
export class ChannelsViewProvider implements TreeDataProvider<PijulChannel | PijulChange> {
  /**
  * Create a new ChannelViewProvider instance
  * @param repository
  */
  constructor (
    private readonly repository: Repository,
    public readonly onDidChangeTreeData?: Event<void>
  ) {}

  /**
  * Convert a PijulChannel into a TreeItem
  * @param element
  */
  getTreeItem (element: PijulChannel | PijulChange): TreeItem | Thenable<TreeItem> {
    if (element instanceof PijulChannel) {
      return {
        id: element.name,
        label: element.name,
        description: element.isCurrent ? 'Current' : undefined,
        collapsibleState: element.isCurrent ? TreeItemCollapsibleState.None : TreeItemCollapsibleState.Collapsed,
        // Different context menu options for current and inactive channels
        contextValue: 'pijulChannel' + (element.isCurrent ? 'Current' : '')
      };
    } else {
      return {
        label: element.message,
        description: `${element.author.name}, ${element.date.toISOString()}`,
        collapsibleState: TreeItemCollapsibleState.None,
        contextValue: 'pijulChange',
        tooltip: `${element.hash}\nRecorded by ${element.author.fullName ?? element.author.name} at ${element.date.toISOString()}\n\n\t${element.message}\n`,
        command: {
          command: 'vscode.open',
          title: 'Open Change TOML',
          arguments: [Uri.parse('pijul-change:' + element.hash)]
        }
      };
    }
  }

  /**
  * Get the children of the element or the root if no element is passed
  * @param element An optional element to get the children of
  */
  async getChildren (element?: PijulChannel | PijulChange): Promise<Array<PijulChannel | PijulChange> | null | undefined> {
    if (element) {
      // Channels don't have children
      if (element instanceof PijulChannel) {
        // TODO: Distinguish between changes in the current channel and not in the other channel and
        // changes in the other channel which are not in the current channel
        return await this.repository.compareChannelChanges(element.name);
      } else {
        return null;
      }
    } else {
      let children;
      // Retry if the pristine is locked.
      while (!children) {
        try {
          children = await this.repository.getChannels();
        } catch (err) {
          if (!(err instanceof Error) || !err.message.includes('Pristine locked')) {
            throw err;
          }
        }
      }
      return children;
    }
  }
}