import type * as Diff2Html from 'diff2html'
// import { Diff2HtmlUI } from 'diff2html/lib-esm/ui/js/diff2html-ui'
import HoganJsUtils from 'diff2html/lib/hoganjs-utils'
// import type { DiffLine } from 'diff2html/lib/types'
import type { CommentItem } from 'components/CommentBox/CommentBox'
import type { TypesPullReqActivity } from 'services/code'
import type { DiffFileEntry } from 'utils/types'

export enum ViewStyle {
  SIDE_BY_SIDE = 'side-by-side',
  LINE_BY_LINE = 'line-by-line'
}

export enum CommentType {
  COMMENT = 'comment',
  CODE_COMMENT = 'code-comment',
  TITLE_CHANGE = 'title-change',
  REVIEW_SUBMIT = 'review-submit',
  MERGE = 'merge'
}

export const PR_CODE_COMMENT_PAYLOAD_VERSION = '0.1'

export interface PullRequestCodeCommentPayload {
  type: CommentType
  version: string // used to avoid rendering old payload structure
  file_id: string // unique id of the changed file
  file_title: string
  language: string
  is_on_left: boolean // comment made on the left side pane
  at_line_number: number
  line_number_range: number[]
  range_text_content: string // raw text content where the comment is made
  diff_html_snapshot: string // snapshot used to render diff in comment (PR Conversation). Could be used to send email notification too (with more work on capturing CSS styles and put them inline)
}

export const DIFF_VIEWER_HEADER_HEIGHT = 36
// const DIFF_MAX_CHANGES = 100
// const DIFF_MAX_LINE_LENGTH = 100

export interface DiffCommentItem<T = Unknown> {
  left: boolean
  right: boolean
  lineNumber: number
  height: number
  commentItems: CommentItem<T>[]
}

export const DIFF2HTML_CONFIG = {
  outputFormat: 'side-by-side',
  drawFileList: false,
  fileListStartVisible: false,
  fileContentToggle: true,
  // diffMaxChanges: DIFF_MAX_CHANGES,
  // diffMaxLineLength: DIFF_MAX_LINE_LENGTH,
  // diffTooBigMessage: index => `${index} - is too big`,
  matching: 'lines',
  synchronisedScroll: true,
  highlight: true,
  renderNothingWhenEmpty: false,
  compiledTemplates: {
    'generic-line': HoganJsUtils.compile(`
      <tr>
        <td class="{{lineClass}} {{type}}">
          {{{lineNumber}}}
        </td>
        <td class="{{type}}" data-content-for-line-number="{{lineNumber}}">
            <div data-annotation-for-line="{{lineNumber}}" tab-index="0" role="button">+</div>
            <div class="{{contentClass}}">
            {{#prefix}}
                <span class="d2h-code-line-prefix">{{{prefix}}}</span>
            {{/prefix}}
            {{^prefix}}
                <span class="d2h-code-line-prefix">&nbsp;</span>
            {{/prefix}}
            {{#content}}
                <span class="d2h-code-line-ctn">{{{content}}}</span>
            {{/content}}
            {{^content}}
                <span class="d2h-code-line-ctn"><br></span>
            {{/content}}
            </div>
        </td>
      </tr>
    `),
    'side-by-side-file-diff': HoganJsUtils.compile(`
      <div id="{{fileHtmlId}}" class="d2h-file-wrapper side-by-side-file-diff" data-lang="{{file.language}}">
        <div class="d2h-file-header">
          {{{filePath}}}
        </div>
        <div class="d2h-files-diff">
            <div class="d2h-file-side-diff left">
                <div class="d2h-code-wrapper">
                    <table class="d2h-diff-table" cellpadding="0px" cellspacing="0px">
                        <tbody class="d2h-diff-tbody">
                        {{{diffs.left}}}
                        </tbody>
                    </table>
                </div>
            </div>
            <div class="d2h-file-side-diff right">
                <div class="d2h-code-wrapper">
                    <table class="d2h-diff-table" cellpadding="0px" cellspacing="0px">
                        <tbody class="d2h-diff-tbody">
                        {{{diffs.right}}}
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
      </div>
    `),
    'line-by-line-file-diff': HoganJsUtils.compile(`
      <div id="{{fileHtmlId}}" class="d2h-file-wrapper line-by-line-file-diff" data-lang="{{file.language}}">
        <div class="d2h-file-header">
        {{{filePath}}}
        </div>
        <div class="d2h-file-diff">
            <div class="d2h-code-wrapper">
                <table class="d2h-diff-table" cellpadding="0px" cellspacing="0px">
                    <tbody class="d2h-diff-tbody">
                    {{{diffs}}}
                    </tbody>
                </table>
            </div>
        </div>
      </div>
    `),
    'line-by-line-numbers': HoganJsUtils.compile(`
      <div class="line-num1" data-line-number="{{oldNumber}}">{{oldNumber}}</div>
      <div class="line-num2" data-line-number="{{newNumber}}">{{newNumber}}</div>
    `)
  }
} as Readonly<Diff2Html.Diff2HtmlConfig>

export function getCommentLineInfo(
  contentDOM: HTMLDivElement | null,
  commentEntry: DiffCommentItem,
  viewStyle: ViewStyle
) {
  const isSideBySideView = viewStyle === ViewStyle.SIDE_BY_SIDE
  const { left, lineNumber } = commentEntry
  const diffBody = contentDOM?.querySelector(
    `${isSideBySideView ? `.d2h-file-side-diff${left ? '.left' : '.right'} ` : ''}.d2h-diff-tbody`
  )
  const rowElement = (
    diffBody?.querySelector(`[data-content-for-line-number="${lineNumber}"]`) ||
    diffBody?.querySelector(
      `${!isSideBySideView ? (left ? '.line-num1' : '.line-num2') : ''}[data-line-number="${lineNumber}"]`
    )
  )?.closest('tr') as HTMLTableRowElement

  let node = rowElement as Element
  let rowPosition = 1
  while (node?.previousElementSibling) {
    rowPosition++
    node = node.previousElementSibling
  }

  let oppositeRowElement: HTMLTableRowElement | null = null

  if (isSideBySideView) {
    const filesDiff = rowElement?.closest('.d2h-files-diff') as HTMLElement
    const sideDiff = filesDiff?.querySelector(`div.${left ? 'right' : 'left'}`) as HTMLElement
    oppositeRowElement = sideDiff?.querySelector(`tr:nth-child(${rowPosition})`)
  }

  return {
    rowElement,
    rowPosition,
    hasCommentsRendered: !!rowElement?.dataset?.annotated,
    oppositeRowElement
  }
}

export function renderCommentOppositePlaceHolder(annotation: DiffCommentItem, oppositeRowElement: HTMLTableRowElement) {
  const placeHolderRow = document.createElement('tr')

  placeHolderRow.dataset.placeHolderForLine = String(annotation.lineNumber)
  placeHolderRow.innerHTML = `
    <td height="${annotation.height}px" class="d2h-code-side-linenumber d2h-code-side-emptyplaceholder d2h-cntx d2h-emptyplaceholder"></td>
    <td class="d2h-cntx d2h-emptyplaceholder" height="${annotation.height}px">
      <div class="d2h-code-side-line d2h-code-side-emptyplaceholder">
        <span class="d2h-code-line-prefix">&nbsp;</span>
        <span class="d2h-code-line-ctn hljs"><br></span>
      </div>
    </td>
  `
  oppositeRowElement.after(placeHolderRow)
}

export const activityToCommentItem = (activity: TypesPullReqActivity): CommentItem<TypesPullReqActivity> => ({
  author: activity.author?.display_name as string,
  created: activity.created as number,
  updated: activity.edited as number,
  deleted: activity.deleted as number,
  content: (activity.text || activity.payload?.Message) as string,
  payload: activity
})

/**
 * Take a small HTML snapshot of a diff in order to render code comment.
 * @param atRow Row element where the comment is placed.
 * @param maxNumberOfLines Maximum number of lines to take.
 * @returns HTML content of the diff.
 */
export function getDiffHTMLSnapshotFromRow(atRow: HTMLTableRowElement, maxNumberOfLines = 5) {
  let linesCapturedCount = 0
  const diffSnapshot = [atRow.outerHTML.trim()]

  let prev = atRow.previousElementSibling

  while (prev && linesCapturedCount < maxNumberOfLines) {
    if (!prev.hasAttribute('data-annotated-line') && !prev.hasAttribute('data-place-holder-for-line')) {
      // Don't count empty lines
      const textContent = prev.textContent?.replace(/\s/g, '')
      if (textContent?.length && textContent !== '+') {
        linesCapturedCount++
      }

      if (textContent !== '+') {
        diffSnapshot.unshift((prev.outerHTML || '').trim())
      }
    }
    prev = prev.previousElementSibling
  }

  return diffSnapshot.join('')
}

// export function getDiffHTMLSnapshotFromDiff(diff: DiffFileEntry, lineNumberRange: number[], isOnLeft: boolean) {
//   const lines = diff?.blocks.reduce((group, item) => {
//     group = group.concat(item.lines)
//     return group
//   }, [] as DiffLine[])

//   const lastIndex = lines.findIndex(line =>
//     lineNumberRange.includes((isOnLeft ? line.oldNumber : line.newNumber) as number)
//   )
//   const startIndex = Math.max(0, lastIndex - 5)

//   const copiedLines = lines.slice(startIndex, lastIndex)
//   const copiedDiff: DiffFileEntry = {
//     ...diff,
//     blocks: [
//       {
//         header: '',
//         lines: copiedLines,
//         newStartLine: copiedLines[0].newNumber as number,
//         oldStartLine: copiedLines[0].oldNumber as number
//       }
//     ]
//   }

//   // console.log({ isOnLeft, startIndex, lastIndex, copiedLines, lines, lineNumberRange })

//   const div = document.createElement('div')
//   new Diff2HtmlUI(div, [copiedDiff], Object.assign({}, DIFF2HTML_CONFIG, { outputFormat: 'line-by-line' })).draw()

//   return div.querySelector('table')?.outerHTML || ''
// }

export function getRawTextInRange(diff: DiffFileEntry, lineNumberRange: number[]) {
  return (
    // TODO: This is wrong, blocks can have multiple items, not one
    (diff?.blocks[0]?.lines || [])
      .filter(line => lineNumberRange.includes(line.newNumber as number))
      .map(line => line.content)
      .join('\n') || ''
  )
}

export function activitiesToDiffCommentItems(diff: DiffFileEntry): DiffCommentItem<TypesPullReqActivity>[] {
  return (
    diff.fileActivities?.map(activity => {
      const payload = activity.payload as PullRequestCodeCommentPayload
      const replyComments =
        diff.activities
          ?.filter(replyActivity => replyActivity.parent_id === activity.id)
          .map(_activity => activityToCommentItem(_activity)) || []

      return {
        left: payload.is_on_left,
        right: !payload.is_on_left,
        height: 0,
        lineNumber: payload.at_line_number,
        commentItems: [activityToCommentItem(activity)].concat(replyComments)
      }
    }) || []
  )
}
