text: // Copyright 2019 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.
  
  import {LitElement, html, css} from 'lit-element';
  
  import 'elements/framework/mr-upload/mr-upload.js';
  import 'elements/framework/mr-error/mr-error.js';
  import {fieldTypes} from 'shared/issue-fields.js';
  import {store, connectStore} from 'reducers/base.js';
  import * as issueV0 from 'reducers/issueV0.js';
  import * as projectV0 from 'reducers/projectV0.js';
  import * as userV0 from 'reducers/userV0.js';
  import 'elements/chops/chops-checkbox/chops-checkbox.js';
  import 'elements/chops/chops-dialog/chops-dialog.js';
  import {SHARED_STYLES, MD_PREVIEW_STYLES, MD_STYLES} from 'shared/shared-styles.js';
  import {commentListToDescriptionList} from 'shared/convertersV0.js';
  import {renderMarkdown, shouldRenderMarkdown} from 'shared/md-helper.js';
  import {unsafeHTML} from 'lit-html/directives/unsafe-html.js';
  
  
  /**
   * `<mr-edit-description>`
   *
   * A dialog to edit descriptions.
   *
   */
  export class MrEditDescription extends connectStore(LitElement) {
    /** @override */
    constructor() {
      super();
      this._editedDescription = '';
    }
  
    /** @override */
    static get styles() {
      return [
        SHARED_STYLES,
        MD_PREVIEW_STYLES,
        MD_STYLES,
        css`
          chops-dialog {
            --chops-dialog-width: 800px;
          }
          textarea {
            font-family: var(--mr-toggled-font-family);
            min-height: 300px;
            max-height: 500px;
            border: var(--chops-accessible-border);
            padding: 0.5em 4px;
            margin: 0.5em 0;
          }
          .attachments {
            margin: 0.5em 0;
          }
          .content {
            padding: 0.5em 0.5em;
            width: 100%;
            box-sizing: border-box;
          }
          .edit-controls {
            display: flex;
            justify-content: space-between;
            align-items: center;
          }
        `,
      ];
    }
  
    /** @override */
    render() {
      return html`
        <link href="https://fonts.googleapis.com/icon?family=Material+Icons"
              rel="stylesheet">
        <chops-dialog aria-labelledby="editDialogTitle">
          <h3 id="editDialogTitle" class="medium-heading">
            Edit ${this._title}
          </h3>
          <textarea
            id="description"
            class="content"
            @keyup=${this._setEditedDescription}
            @change=${this._setEditedDescription}
            .value=${this._editedDescription}
          ></textarea>
          ${this._renderMarkdown ? html`
            <div class="markdown-preview preview-height-description">
              <div class="markdown">
                ${unsafeHTML(renderMarkdown(this._editedDescription))}
              </div>
            </div>`: ''}
          <h3 class="medium-heading">
            Add attachments
          </h3>
          <div class="attachments">
            ${this._attachments && this._attachments.map((attachment) => html`
              <label>
                <chops-checkbox
                  type="checkbox"
                  checked="true"
                  class="kept-attachment"
                  data-attachment-id=${attachment.attachmentId}
                  @checked-change=${this._keptAttachmentIdsChanged}
                />
                <a href=${attachment.viewUrl} target="_blank">
                  ${attachment.filename}
                </a>
              </label>
              <br>
            `)}
            <mr-upload></mr-upload>
          </div>
          <mr-error
            ?hidden=${!this._attachmentError}
          >${this._attachmentError}</mr-error>
          <div class="edit-controls">
            <chops-checkbox
              id="sendEmail"
              ?checked=${this._sendEmail}
              @checked-change=${this._setSendEmail}
            >Send email</chops-checkbox>
            <div>
              <chops-button id="discard" @click=${this.cancel} class="de-emphasized">
                Discard
              </chops-button>
              <chops-button id="save" @click=${this.save} class="emphasized">
                Save changes
              </chops-button>
            </div>
          </div>
        </chops-dialog>
      `;
    }
  
    /** @override */
    static get properties() {
      return {
        commentsByApproval: {type: Array},
        issueRef: {type: Object},
        fieldName: {type: String},
        projectName: {type: String},
        _attachmentError: {type: String},
        _attachments: {type: Array},
        _boldLines: {type: Array},
        _editedDescription: {type: String},
        _title: {type: String},
        _keptAttachmentIds: {type: Object},
        _sendEmail: {type: Boolean},
        _prefs: {type: Object},
      };
    }
  
    /** @override */
    stateChanged(state) {
      this.commentsByApproval = issueV0.commentsByApprovalName(state);
      this.issueRef = issueV0.viewedIssueRef(state);
      this.projectName = projectV0.viewedProjectName(state);
      this._prefs = userV0.prefs(state);
    }
  
    /**
     * Public function to open the issue description editing dialog.
     * @param {Event} e
     */
    async open(e) {
      await this.updateComplete;
      this.shadowRoot.querySelector('chops-dialog').open();
      this.fieldName = e.detail.fieldName;
      this.reset();
    }
  
    /**
     * Resets edit form.
     */
    async reset() {
      await this.updateComplete;
      this._attachmentError = '';
      this._attachments = [];
      this._boldLines = [];
      this._keptAttachmentIds = new Set();
  
      const uploader = this.shadowRoot.querySelector('mr-upload');
      if (uploader) {
        uploader.reset();
      }
  
      // Sets _editedDescription and _title.
      this._initializeView(this.commentsByApproval, this.fieldName);
  
      this.shadowRoot.querySelectorAll('.kept-attachment').forEach((checkbox) => {
        checkbox.checked = true;
      });
      this.shadowRoot.querySelector('#sendEmail').checked = true;
  
      this._sendEmail = true;
    }
  
    /**
     * Cancels in-flight edit data.
     */
    async cancel() {
      await this.updateComplete;
      this.shadowRoot.querySelector('chops-dialog').close();
    }
  
    /**
     * Sends the user's edit to Monorail's backend to be saved.
     */
    async save() {
      const commentContent = this._markupNewContent();
      const sendEmail = this._sendEmail;
      const keptAttachments = Array.from(this._keptAttachmentIds);
      const message = {
        issueRef: this.issueRef,
        isDescription: true,
        commentContent,
        keptAttachments,
        sendEmail,
      };
  
      try {
        const uploader = this.shadowRoot.querySelector('mr-upload');
        const uploads = await uploader.loadFiles();
        if (uploads && uploads.length) {
          message.uploads = uploads;
        }
  
        if (!this.fieldName) {
          store.dispatch(issueV0.update(message));
        } else {
          // This is editing an approval if there is no field name.
          message.fieldRef = {
            type: fieldTypes.APPROVAL_TYPE,
            fieldName: this.fieldName,
          };
          store.dispatch(issueV0.updateApproval(message));
        }
        this.shadowRoot.querySelector('chops-dialog').close();
      } catch (e) {
        this._attachmentError = `Error while loading file for attachment: ${
          e.message}`;
      }
    }
  
    /**
     * Getter for checking if the user has Markdown enabled.
     * @return {boolean} Whether Markdown preview should be rendered or not.
     */
     get _renderMarkdown() {
      const enabled = this._prefs.get('render_markdown');
      return shouldRenderMarkdown({project: this.projectName, enabled});
    }
  
    /**
     * Event handler for keeping <mr-edit-description>'s copy of
     * _editedDescription in sync.
     * @param {Event} e
     */
    _setEditedDescription(e) {
      const target = e.target;
      this._editedDescription = target.value;
    }
  
    /**
     * Event handler for keeping attachment state in sync.
     * @param {Event} e
     */
    _keptAttachmentIdsChanged(e) {
      e.target.checked = e.detail.checked;
      const attachmentId = Number.parseInt(e.target.dataset.attachmentId);
      if (e.target.checked) {
        this._keptAttachmentIds.add(attachmentId);
      } else {
        this._keptAttachmentIds.delete(attachmentId);
      }
    }
  
    _initializeView(commentsByApproval, fieldName) {
      this._title = fieldName ? `${fieldName} Survey` : 'Description';
      const key = fieldName || '';
      if (!commentsByApproval || !commentsByApproval.has(key)) return;
      const comments = commentListToDescriptionList(commentsByApproval.get(key));
  
      const comment = comments[comments.length - 1];
  
      if (comment.attachments) {
        this._keptAttachmentIds = new Set(comment.attachments.map(
            (attachment) => Number.parseInt(attachment.attachmentId)));
        this._attachments = comment.attachments;
      }
  
      this._processRawContent(comment.content);
    }
  
    _processRawContent(content) {
      const chunks = content.trim().split(/(<b>[^<\n]+<\/b>)/m);
      const boldLines = [];
      let cleanContent = '';
      chunks.forEach((chunk) => {
        if (chunk.startsWith('<b>') && chunk.endsWith('</b>')) {
          const cleanChunk = chunk.slice(3, -4).trim();
          cleanContent += cleanChunk;
          // Don't add whitespace to boldLines.
          if (/\S/.test(cleanChunk)) {
            boldLines.push(cleanChunk);
          }
        } else {
          cleanContent += chunk;
        }
      });
  
      this._boldLines = boldLines;
      this._editedDescription = cleanContent;
    }
  
    _markupNewContent() {
      const lines = this._editedDescription.trim().split('\n');
      const markedLines = lines.map((line) => {
        let markedLine = line;
        const matchingBoldLine = this._boldLines.find(
            (boldLine) => (line.startsWith(boldLine)));
        if (matchingBoldLine) {
          markedLine =
            `<b>${matchingBoldLine}</b>${line.slice(matchingBoldLine.length)}`;
        }
        return markedLine;
      });
      return markedLines.join('\n');
    }
  
    /**
     * Event handler for keeping email state in sync.
     * @param {Event} e
     */
    _setSendEmail(e) {
      this._sendEmail = e.detail.checked;
    }
  }
  
  customElements.define('mr-edit-description', MrEditDescription);
  