import { html } from 'lit'
import { classMap } from 'lit/directives/class-map.js'
import { ifDefined } from 'lit/directives/if-defined.js'

import { getDisplayFilename, splitFilenameForTruncation } from 'shared-utils/fileupload'

import type {
  FileItem,
  TQueueItemOperation,
  TQueueOperationContext,
  TTransferProgress,
} from './fileupload-types'

type TRenderQueueItemProps = {
  file: FileItem
  inputName: string
  disabled: boolean
  fileSize: string
  thumbnailUrl?: string
  previewEnabled?: boolean
  canOpenPreview?: boolean
  transferProgress: TTransferProgress
  transferErrorMessage?: string
  transferShowProgress?: boolean
  transferLastProgress?: number
  loadingText?: string
  operations: TQueueItemOperation[]
  activeOperationId?: string
  truncateTail?: number
  onActivateOperation: (fileId: string, operationId: string) => void
  onCloseOperation: (fileId: string) => void
  getFileAttribute: <T>(fileId: string, name: string) => T | undefined
  setFileAttribute: (fileId: string, name: string, value: unknown) => void
  onCancel: (fileId: string) => void
  onOpenPreview?: (fileId: string) => void
  onThumbnailImageError?: (fileId: string) => void
}

/**
 * Render a filename with optional middle-truncation. The split-or-not decision
 * lives in `splitFilenameForTruncation` (shared with React) so the threshold
 * matches across both runtimes.
 */
const renderTruncatedFilename = (filename: string, truncateTail?: number) => {
  const split = splitFilenameForTruncation(filename, truncateTail)
  if (!split) return html`<span data-pkt-truncate-part="first">${filename}</span>`
  return html`<span
      class="pkt-fileupload__queue-display__item__title__head"
      data-pkt-truncate-part="first"
      >${split.head}</span
    ><span
      class="pkt-fileupload__queue-display__item__title__tail"
      data-pkt-truncate-part="tail"
      >${split.tail}</span
    >`
}

const renderTitleWithSize = (filename: string, fileSize: string, truncateTail?: number) => html`
  <p class="pkt-fileupload__queue-display__item__title">
    ${renderTruncatedFilename(filename, truncateTail)}${fileSize
      ? html`<span class="pkt-fileupload__queue-display__item__filesize"> (${fileSize})</span>`
      : null}
  </p>
`

const getProgressState = (progress: TTransferProgress): 'in-progress' | 'error' | 'idle' => {
  if (typeof progress === 'number') return 'in-progress'
  if (progress === 'error') return 'error'
  return 'idle'
}

const renderLoadingText = (loadingText?: string) =>
  html`<span class="pkt-fileupload__queue-display__item__loading-text" aria-label="Laster opp fil"
    >${loadingText ?? 'Laster opp...'}</span
  >`

const createOperationContext = ({
  operationId,
  file,
  inputName,
  disabled,
  isActive,
  onActivateOperation,
  onCloseOperation,
  getFileAttribute,
  setFileAttribute,
}: {
  operationId: string
  file: FileItem
  inputName: string
  disabled: boolean
  isActive: boolean
  onActivateOperation: (fileId: string, operationId: string) => void
  onCloseOperation: (fileId: string) => void
  getFileAttribute: <T>(fileId: string, name: string) => T | undefined
  setFileAttribute: (fileId: string, name: string, value: unknown) => void
}): TQueueOperationContext => ({
  file,
  inputName,
  disabled,
  isActive,
  activate: () => onActivateOperation(file.fileId, operationId),
  close: () => onCloseOperation(file.fileId),
  getAttribute: <T>(name: string) => getFileAttribute<T>(file.fileId, name),
  setAttribute: (name: string, value: unknown) => setFileAttribute(file.fileId, name, value),
})

const renderTransferInProgress = ({
  file,
  fileSize,
  disabled,
  transferProgress,
  transferShowProgress,
  loadingText,
  truncateTail,
  onCancel,
  iconName = 'document-text',
}: {
  file: FileItem
  fileSize: string
  disabled: boolean
  transferProgress: number
  transferShowProgress?: boolean
  loadingText?: string
  truncateTail?: number
  onCancel: (fileId: string) => void
  iconName?: string
}) => {
  const filename = getDisplayFilename(file)
  const showProgressBar = transferShowProgress ?? transferProgress !== 0

  return html`
    <pkt-icon
      name=${iconName}
      class="pkt-fileupload__queue-display__item__icon pkt-icon--medium"
      aria-hidden="true"
    ></pkt-icon>
    ${renderTitleWithSize(filename, fileSize, truncateTail)}
    <button
      type="button"
      class="pkt-fileupload__queue-display__item__cancel-button"
      aria-label="Avbryt opplasting"
      ?disabled=${disabled}
      @click=${() => onCancel(file.fileId)}
    >
      Avbryt
    </button>
    ${showProgressBar
      ? html`
          <pkt-progressbar
            class="pkt-fileupload__queue-display__item__progress"
            valueCurrent=${Math.round(transferProgress * 100)}
            valueMax=${100}
            statusType="none"
            statusPlacement="following"
          ></pkt-progressbar>
          <span class="pkt-fileupload__queue-display__item__percentage" aria-hidden="true"
            >${Math.round(transferProgress * 100)}%</span
          >
        `
      : renderLoadingText(loadingText)}
  `
}

const renderTransferError = ({
  file,
  fileSize,
  disabled,
  transferShowProgress,
  transferLastProgress,
  transferErrorMessage,
  truncateTail,
  onCancel,
}: {
  file: FileItem
  fileSize: string
  disabled: boolean
  transferShowProgress?: boolean
  transferLastProgress?: number
  transferErrorMessage?: string
  truncateTail?: number
  onCancel: (fileId: string) => void
}) => {
  const filename = getDisplayFilename(file)
  const showProgressError = !!transferShowProgress
  const progressValue = Math.round((transferLastProgress ?? 1) * 100)

  return html`
    <pkt-icon
      name="alert-error"
      class="pkt-fileupload__queue-display__item__icon pkt-icon--medium"
      aria-hidden="true"
    ></pkt-icon>
    ${renderTitleWithSize(filename, fileSize, truncateTail)}
    <button
      type="button"
      class="pkt-fileupload__queue-display__item__cancel-button"
      aria-label="Fjern fil som feilet under opplasting"
      ?disabled=${disabled}
      @click=${() => onCancel(file.fileId)}
    >
      Fjern
    </button>
    ${showProgressError
      ? html`
          <pkt-progressbar
            class="pkt-fileupload__queue-display__item__progress pkt-fileupload__queue-display__item__progress--error"
            valueCurrent=${progressValue}
            valueMax=${100}
            statusType="none"
            statusPlacement="following"
          ></pkt-progressbar>
        `
      : null}
    ${transferErrorMessage
      ? html`<span class="pkt-fileupload__queue-display__item__error-message"
          >${transferErrorMessage}</span
        >`
      : null}
  `
}

const renderOperationButtons = ({
  file,
  disabled,
  inputName,
  operations,
  activeOperationId,
  onActivateOperation,
  onCloseOperation,
  getFileAttribute,
  setFileAttribute,
}: {
  file: FileItem
  disabled: boolean
  inputName: string
  operations: TQueueItemOperation[]
  activeOperationId?: string
  onActivateOperation: (fileId: string, operationId: string) => void
  onCloseOperation: (fileId: string) => void
  getFileAttribute: <T>(fileId: string, name: string) => T | undefined
  setFileAttribute: (fileId: string, name: string, value: unknown) => void
}) => {
  const activeOperation = operations.find((operation) => operation.id === activeOperationId)
  const hasOperationUIActive = !!(
    activeOperation?.renderInlineUI || activeOperation?.renderExtendedUI
  )
  if (hasOperationUIActive) return null

  const operationButtons = operations
    .map((operation) => {
      const context = createOperationContext({
        operationId: operation.id,
        file,
        inputName,
        disabled,
        isActive: operation.id === activeOperationId,
        onActivateOperation,
        onCloseOperation,
        getFileAttribute,
        setFileAttribute,
      })
      const title = typeof operation.title === 'function' ? operation.title(file) : operation.title
      if (!title) return null
      const ariaLabel =
        typeof operation.ariaLabel === 'function' ? operation.ariaLabel(file) : operation.ariaLabel
      return html`<button
        type="button"
        class="pkt-fileupload__queue-display__item__operation"
        aria-label=${ifDefined(ariaLabel)}
        ?disabled=${disabled}
        @click=${() => {
          if (operation.renderInlineUI || operation.renderExtendedUI) {
            context.activate()
          }
          operation.onClick?.(context)
        }}
      >
        ${title}
      </button>`
    })
    .filter(Boolean)

  if (operationButtons.length === 0) return null
  return html`<div class="pkt-fileupload__queue-display__item__actions">${operationButtons}</div>`
}

const renderOperationContent = ({
  file,
  disabled,
  inputName,
  operations,
  activeOperationId,
  onActivateOperation,
  onCloseOperation,
  getFileAttribute,
  setFileAttribute,
}: {
  file: FileItem
  disabled: boolean
  inputName: string
  operations: TQueueItemOperation[]
  activeOperationId?: string
  onActivateOperation: (fileId: string, operationId: string) => void
  onCloseOperation: (fileId: string) => void
  getFileAttribute: <T>(fileId: string, name: string) => T | undefined
  setFileAttribute: (fileId: string, name: string, value: unknown) => void
}) =>
  operations.map((operation) => {
    if (!operation.renderContent) return null
    const context = createOperationContext({
      operationId: operation.id,
      file,
      inputName,
      disabled,
      isActive: operation.id === activeOperationId,
      onActivateOperation,
      onCloseOperation,
      getFileAttribute,
      setFileAttribute,
    })
    const content = operation.renderContent(context)
    if (content === null || content === undefined) {
      return null
    }
    return html`<div class="pkt-fileupload__queue-display__item__operation-content">
      ${content}
    </div>`
  })

const renderExpandedOperationContent = ({
  file,
  disabled,
  inputName,
  operations,
  activeOperationId,
  onActivateOperation,
  onCloseOperation,
  getFileAttribute,
  setFileAttribute,
}: {
  file: FileItem
  disabled: boolean
  inputName: string
  operations: TQueueItemOperation[]
  activeOperationId?: string
  onActivateOperation: (fileId: string, operationId: string) => void
  onCloseOperation: (fileId: string) => void
  getFileAttribute: <T>(fileId: string, name: string) => T | undefined
  setFileAttribute: (fileId: string, name: string, value: unknown) => void
}) => {
  if (!activeOperationId) return null
  const activeOperation = operations.find((operation) => operation.id === activeOperationId)
  if (!activeOperation?.renderExtendedUI) return null
  const context = createOperationContext({
    operationId: activeOperation.id,
    file,
    inputName,
    disabled,
    isActive: true,
    onActivateOperation,
    onCloseOperation,
    getFileAttribute,
    setFileAttribute,
  })
  return html`<div class="pkt-fileupload__queue-display__item__expanded-operation-ui">
    ${activeOperation.renderExtendedUI(context)}
  </div>`
}

const renderHiddenOperationInputs = ({
  file,
  disabled,
  inputName,
  operations,
  activeOperationId,
  onActivateOperation,
  onCloseOperation,
  getFileAttribute,
  setFileAttribute,
}: {
  file: FileItem
  disabled: boolean
  inputName: string
  operations: TQueueItemOperation[]
  activeOperationId?: string
  onActivateOperation: (fileId: string, operationId: string) => void
  onCloseOperation: (fileId: string) => void
  getFileAttribute: <T>(fileId: string, name: string) => T | undefined
  setFileAttribute: (fileId: string, name: string, value: unknown) => void
}) =>
  operations.map((operation) => {
    if (!operation.renderHidden) return null
    const context = createOperationContext({
      operationId: operation.id,
      file,
      inputName,
      disabled,
      isActive: operation.id === activeOperationId,
      onActivateOperation,
      onCloseOperation,
      getFileAttribute,
      setFileAttribute,
    })
    return operation.renderHidden(context)
  })

const renderFilenameIdleMain = (file: FileItem, fileSize: string, truncateTail?: number) => {
  const filename = getDisplayFilename(file)
  return html`
    <pkt-icon
      name="document-text"
      class="pkt-fileupload__queue-display__item__icon pkt-icon--medium"
      aria-hidden="true"
    ></pkt-icon>
    ${renderTitleWithSize(filename, fileSize, truncateTail)}
  `
}

const renderThumbnailIdleMain = ({
  file,
  thumbnailUrl,
  previewEnabled,
  canOpenPreview,
  truncateTail,
  onOpenPreview,
  onThumbnailImageError,
}: {
  file: FileItem
  thumbnailUrl?: string
  previewEnabled?: boolean
  canOpenPreview?: boolean
  truncateTail?: number
  onOpenPreview?: (fileId: string) => void
  onThumbnailImageError?: (fileId: string) => void
}) => {
  const filename = getDisplayFilename(file)
  const hasThumbnail = !!thumbnailUrl
  const showExpandButton = !!previewEnabled && !!canOpenPreview
  const isInteractive = showExpandButton && !!onOpenPreview

  return html`
    <div
      class=${classMap({
        'pkt-fileupload__queue-display__item__thumbnail': true,
        'pkt-fileupload__queue-display__item__thumbnail--fallback': !hasThumbnail,
      })}
    >
      <button
        type="button"
        class="pkt-fileupload__queue-display__item__thumbnail__image-wrapper pkt-btn pkt-btn--medium pkt-btn--secondary pkt-btn--label-only"
        aria-label=${`Forhåndsvis bilde ${filename}`}
        ?disabled=${!isInteractive}
        @click=${() => {
          if (!isInteractive || !onOpenPreview) return
          onOpenPreview(file.fileId)
        }}
      >
        <span class="pkt-btn__text">
          ${showExpandButton
            ? html`<pkt-icon
                name="expand"
                class="pkt-fileupload__queue-display__item__thumbnail__expand-icon"
                aria-hidden="true"
              ></pkt-icon>`
            : null}
          ${hasThumbnail
            ? html`<img
                src=${ifDefined(thumbnailUrl)}
                alt=${filename}
                @error=${() => onThumbnailImageError?.(file.fileId)}
              />`
            : html`<pkt-icon
                name="document-text"
                class="pkt-fileupload__queue-display__item__icon pkt-icon--medium"
                aria-hidden="true"
              ></pkt-icon>`}
        </span>
      </button>
      <span class="pkt-fileupload__queue-display__item__thumbnail__title"
        >${renderTruncatedFilename(filename, truncateTail)}</span
      >
    </div>
  `
}

export const renderFilenameQueueItem = ({
  file,
  inputName,
  disabled,
  fileSize,
  operations,
  activeOperationId,
  onActivateOperation,
  onCloseOperation,
  getFileAttribute,
  setFileAttribute,
  transferProgress,
  transferErrorMessage,
  transferShowProgress,
  transferLastProgress,
  loadingText,
  truncateTail,
  onCancel,
}: TRenderQueueItemProps) => {
  const state = getProgressState(transferProgress)
  const activeOperation = operations.find((operation) => operation.id === activeOperationId)
  const inlineOperationTemplate = activeOperation?.renderInlineUI
    ? activeOperation.renderInlineUI(
        createOperationContext({
          operationId: activeOperation.id,
          file,
          inputName,
          disabled,
          isActive: true,
          onActivateOperation,
          onCloseOperation,
          getFileAttribute,
          setFileAttribute,
        }),
      )
    : null

  return html`
    <li
      class=${classMap({
        'pkt-fileupload__queue-display__item': true,
        'pkt-fileupload__queue-display__item--in-progress': state === 'in-progress',
        'pkt-fileupload__queue-display__item--error': state === 'error',
        [`pkt-fileupload__queue-display__item--${transferProgress}`]:
          typeof transferProgress === 'string',
      })}
    >
      ${renderHiddenOperationInputs({
        file,
        disabled,
        inputName,
        operations,
        activeOperationId,
        onActivateOperation,
        onCloseOperation,
        getFileAttribute,
        setFileAttribute,
      })}
      ${state === 'in-progress'
        ? renderTransferInProgress({
            file,
            fileSize,
            disabled,
            transferProgress: transferProgress as number,
            transferShowProgress,
            loadingText,
            truncateTail,
            onCancel,
          })
        : state === 'error'
          ? renderTransferError({
              file,
              fileSize,
              disabled,
              transferShowProgress,
              transferLastProgress,
              transferErrorMessage,
              truncateTail,
              onCancel,
            })
          : inlineOperationTemplate
            ? html`
                <pkt-icon
                  name="document-text"
                  class="pkt-fileupload__queue-display__item__icon pkt-icon--medium"
                  aria-hidden="true"
                ></pkt-icon>
                <div class="pkt-fileupload__queue-display__item__inline-ui">
                  ${inlineOperationTemplate}
                </div>
              `
            : renderFilenameIdleMain(file, fileSize, truncateTail)}
      ${state === 'idle'
        ? renderOperationButtons({
            file,
            disabled,
            inputName,
            operations,
            activeOperationId,
            onActivateOperation,
            onCloseOperation,
            getFileAttribute,
            setFileAttribute,
          })
        : null}
      ${state === 'idle'
        ? renderOperationContent({
            file,
            disabled,
            inputName,
            operations,
            activeOperationId,
            onActivateOperation,
            onCloseOperation,
            getFileAttribute,
            setFileAttribute,
          })
        : null}
      ${state === 'idle'
        ? renderExpandedOperationContent({
            file,
            disabled,
            inputName,
            operations,
            activeOperationId,
            onActivateOperation,
            onCloseOperation,
            getFileAttribute,
            setFileAttribute,
          })
        : null}
    </li>
  `
}

export const renderThumbnailQueueItem = ({
  file,
  inputName,
  disabled,
  fileSize,
  thumbnailUrl,
  previewEnabled,
  canOpenPreview,
  operations,
  activeOperationId,
  onActivateOperation,
  onCloseOperation,
  getFileAttribute,
  setFileAttribute,
  transferProgress,
  transferErrorMessage,
  transferShowProgress,
  transferLastProgress,
  loadingText,
  truncateTail,
  onCancel,
  onOpenPreview,
  onThumbnailImageError,
}: TRenderQueueItemProps) => {
  const state = getProgressState(transferProgress)
  const activeOperation = operations.find((operation) => operation.id === activeOperationId)
  const inlineOperationTemplate = activeOperation?.renderInlineUI
    ? activeOperation.renderInlineUI(
        createOperationContext({
          operationId: activeOperation.id,
          file,
          inputName,
          disabled,
          isActive: true,
          onActivateOperation,
          onCloseOperation,
          getFileAttribute,
          setFileAttribute,
        }),
      )
    : null

  return html`
    <li
      class=${classMap({
        'pkt-fileupload__queue-display__item': true,
        'pkt-fileupload__queue-display__item--in-progress': state === 'in-progress',
        'pkt-fileupload__queue-display__item--error': state === 'error',
        [`pkt-fileupload__queue-display__item--${transferProgress}`]:
          typeof transferProgress === 'string',
      })}
    >
      ${renderHiddenOperationInputs({
        file,
        disabled,
        inputName,
        operations,
        activeOperationId,
        onActivateOperation,
        onCloseOperation,
        getFileAttribute,
        setFileAttribute,
      })}
      ${state === 'in-progress'
        ? renderTransferInProgress({
            file,
            fileSize,
            disabled,
            transferProgress: transferProgress as number,
            transferShowProgress,
            loadingText,
            truncateTail,
            onCancel,
            iconName: 'picture',
          })
        : state === 'error'
          ? renderTransferError({
              file,
              fileSize,
              disabled,
              transferShowProgress,
              transferLastProgress,
              transferErrorMessage,
              truncateTail,
              onCancel,
            })
          : inlineOperationTemplate
            ? html`
                <pkt-icon
                  name="picture"
                  class="pkt-fileupload__queue-display__item__icon pkt-icon--medium"
                  aria-hidden="true"
                ></pkt-icon>
                <div class="pkt-fileupload__queue-display__item__inline-ui">
                  ${inlineOperationTemplate}
                </div>
              `
            : renderThumbnailIdleMain({
                file,
                thumbnailUrl,
                previewEnabled,
                canOpenPreview,
                truncateTail,
                onOpenPreview,
                onThumbnailImageError,
              })}
      ${state === 'idle'
        ? renderOperationButtons({
            file,
            disabled,
            inputName,
            operations,
            activeOperationId,
            onActivateOperation,
            onCloseOperation,
            getFileAttribute,
            setFileAttribute,
          })
        : null}
      ${state === 'idle'
        ? renderOperationContent({
            file,
            disabled,
            inputName,
            operations,
            activeOperationId,
            onActivateOperation,
            onCloseOperation,
            getFileAttribute,
            setFileAttribute,
          })
        : null}
      ${state === 'idle'
        ? renderExpandedOperationContent({
            file,
            disabled,
            inputName,
            operations,
            activeOperationId,
            onActivateOperation,
            onCloseOperation,
            getFileAttribute,
            setFileAttribute,
          })
        : null}
    </li>
  `
}
