import { forwardRef, useState } from 'react'
import type { HTMLAttributes, KeyboardEvent, MouseEvent, ReactNode } from 'react'
import { cn } from '@/lib/utils'
import {
  CloseLine,
  CodeSSlashLine,
  FileLine,
  FileList2Line,
  TerminalBoxLine,
  WarningLine,
} from '../basic/icons-inline'
import { Tooltip } from '../basic/tooltip'
import { getFileIconColorClass } from '../lib/file-icon-maps'

const ICON_CLASS = 'h-5 w-5'
const CODE_EXTENSIONS = new Set(['js', 'jsx', 'ts', 'tsx', 'html', 'css', 'scss', 'sass', 'less', 'vue', 'svelte'])
const TERMINAL_EXTENSIONS = new Set(['sh', 'bash', 'zsh', 'fish'])
const DOC_EXTENSIONS = new Set(['md', 'mdx', 'txt', 'doc', 'docx', 'pdf', 'ppt', 'pptx'])

export type FileCardVariant = 'default' | 'attachment'
export type FileCardType = 'file' | 'folder'

export interface FileCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
  variant?: FileCardVariant
  filename: string
  iconFilename?: string
  size?: string
  icon?: ReactNode
  getFileIcon?: (filename: string, className?: string) => ReactNode
  onClick?: () => void
  suffixAction?: ReactNode
  path?: string
  type?: FileCardType
  typeLabel?: string
  deleted?: boolean
  deletedTooltip?: string
  onRemove?: (event: MouseEvent<HTMLButtonElement>) => void
}

function getExtension(filename: string): string {
  const lastDot = filename.lastIndexOf('.')
  if (lastDot <= 0 || lastDot === filename.length - 1) return ''
  return filename.slice(lastDot + 1).toLowerCase()
}

function getDefaultTypeLabel(filename: string, type: FileCardType): string {
  if (type === 'folder') return 'FOLDER'
  const ext = getExtension(filename)
  return ext ? ext.toUpperCase() : 'FILE'
}

function buildMetadata(typeLabel: string, size?: string): string {
  return size ? `${typeLabel} · ${size}` : typeLabel
}

function getDefaultIcon(filename: string, className: string): ReactNode {
  const extension = getExtension(filename)
  if (CODE_EXTENSIONS.has(extension)) return <CodeSSlashLine className={className} />
  if (TERMINAL_EXTENSIONS.has(extension)) return <TerminalBoxLine className={className} />
  if (DOC_EXTENSIONS.has(extension)) return <FileList2Line className={className} />
  return <FileLine className={className} />
}

function FileCover({
  filename,
  type,
  deleted,
  icon,
  getFileIcon,
}: {
  filename: string
  type: FileCardType
  deleted?: boolean
  icon?: ReactNode
  getFileIcon?: (filename: string, className?: string) => ReactNode
}) {
  if (type === 'folder') {
    if (deleted) {
      return (
        <div className="absolute left-2 top-1/2 flex size-14 -translate-y-1/2 items-center justify-center">
          <WarningLine className="size-6 text-error" />
        </div>
      )
    }

    return (
      <div className="absolute left-2 top-1/2 size-14 -translate-y-1/2" aria-hidden>
        <svg className="size-full" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
          <path
            d="M7.72754 42.656C7.72754 43.5839 8.4797 44.336 9.40754 44.336H46.0315C46.9594 44.336 47.7115 43.5839 47.7115 42.656V17.2529C47.7115 16.3251 46.9594 15.5729 46.0315 15.5729H24.6575C23.5557 15.5729 22.5152 15.0662 21.8359 14.1988L21.5494 13.8331C20.8488 12.9386 19.7758 12.416 18.6396 12.416H9.40754C8.4797 12.416 7.72754 13.1682 7.72754 14.096V42.656Z"
            fill="var(--color-info)"
          />
          <path
            d="M45.808 16.672H9.63196C9.26083 16.672 8.95996 16.9729 8.95996 17.344V43.104C8.95996 43.4751 9.26083 43.776 9.63196 43.776H45.808C46.1791 43.776 46.48 43.4751 46.48 43.104V17.344C46.48 16.9729 46.1791 16.672 45.808 16.672Z"
            fill="var(--color-bg-container)"
          />
          <path
            d="M47.0395 18.576H8.39954C8.0284 18.576 7.72754 18.8769 7.72754 19.248V43.664C7.72754 44.0351 8.0284 44.336 8.39954 44.336H47.0395C47.4107 44.336 47.7115 44.0351 47.7115 43.664V19.248C47.7115 18.8769 47.4107 18.576 47.0395 18.576Z"
            fill="var(--color-info-hover)"
          />
        </svg>
      </div>
    )
  }

  const iconClass = cn(ICON_CLASS, deleted ? 'text-error' : getFileIconColorClass(undefined, filename))
  const resolvedIcon = deleted ? (
    <WarningLine className={iconClass} />
  ) : (
    icon ?? (getFileIcon ? getFileIcon(filename, iconClass) : getDefaultIcon(filename, iconClass))
  )

  return (
    <div className="absolute left-3 top-2 h-14 w-12">
      <div className="relative h-full w-full overflow-hidden rounded-tl rounded-tr-2xl border-x border-t border-border-tertiary bg-bg-base shadow-sm">
        <div className="absolute left-1/2 top-[calc(50%+3px)] flex h-7 w-7 -translate-x-1/2 -translate-y-1/2 items-center justify-center">
          {resolvedIcon}
        </div>
        <div className="absolute left-8 top-0 h-4 w-4 rounded-bl border-b border-l border-border-tertiary bg-fill-secondary" />
      </div>
    </div>
  )
}

export const FileCard = forwardRef<HTMLDivElement, FileCardProps>(
  (
    {
      variant = 'default',
      filename,
      iconFilename,
      size,
      icon,
      getFileIcon,
      onClick,
      suffixAction,
      path,
      type = 'file',
      typeLabel,
      deleted = false,
      deletedTooltip = 'File has been deleted',
      onRemove,
      className,
      onMouseEnter,
      onMouseLeave,
      ...props
    },
    ref
  ) => {
    const iconLookup = iconFilename ?? filename
    const [isHovered, setIsHovered] = useState(false)
    const metadata = buildMetadata(typeLabel ?? getDefaultTypeLabel(iconLookup, type), size)
    const interactive = Boolean(onClick) && !deleted
    const tooltipContent = deleted ? deletedTooltip : path

    const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
      if (!interactive) return
      if (event.key === 'Enter' || event.key === ' ') {
        event.preventDefault()
        onClick?.()
      }
    }

    const handleRemove = (event: MouseEvent<HTMLButtonElement>) => {
      event.preventDefault()
      event.stopPropagation()
      onRemove?.(event)
    }

    const content = (
      <>
        <div className="absolute left-0 top-0 h-full w-20 origin-bottom transition-transform duration-200 ease-out group-hover/file-card:-rotate-[5deg]">
          <FileCover filename={iconLookup} type={type} deleted={deleted} icon={icon} getFileIcon={getFileIcon} />
        </div>
        <div className="absolute -bottom-px -left-px h-3 w-20 bg-gradient-to-b from-transparent to-bg-base" />

        <div className="ml-20 flex min-w-0 flex-1 flex-col items-start justify-center gap-1">
          <div
            className={cn(
              'w-full truncate text-sm font-semibold leading-[22px] text-text',
              deleted && 'text-text-tertiary line-through'
            )}
            title={filename}
          >
            {filename}
          </div>
          <div className="w-full truncate text-xs leading-[18px] text-text-quaternary" title={path ?? metadata}>
            {metadata}
          </div>
        </div>

        {suffixAction != null && (
          <div
            data-file-card-suffix
            className="ml-auto flex shrink-0 items-center self-stretch text-text-tertiary"
            onClick={(event: MouseEvent<HTMLDivElement>) => event.stopPropagation()}
          >
            {suffixAction}
          </div>
        )}
      </>
    )

    if (variant === 'attachment') {
      const attachment = (
        <div
          ref={ref}
          {...props}
          className={cn('relative max-w-full cursor-pointer', className)}
          role={interactive ? 'button' : undefined}
          tabIndex={interactive ? 0 : undefined}
          aria-disabled={deleted || undefined}
          onClick={interactive ? onClick : undefined}
          onKeyDown={handleKeyDown}
          onMouseEnter={(event) => {
            setIsHovered(true)
            onMouseEnter?.(event)
          }}
          onMouseLeave={(event) => {
            setIsHovered(false)
            onMouseLeave?.(event)
          }}
        >
          <div
            className={cn(
              'group/file-card relative flex h-16 min-w-44 max-w-full items-center overflow-hidden rounded-lg border border-border-tertiary/60 bg-bg-base pr-4',
              'transition-colors duration-200 hover:border-border-secondary',
              deleted && 'border-error-border/60 opacity-60'
            )}
          >
            {content}
          </div>

          {onRemove != null && (
            <button
              type="button"
              aria-label={`Remove ${filename}`}
              onClick={handleRemove}
              className={cn(
                'absolute -right-1.5 -top-1.5 z-10 flex size-4 cursor-pointer items-center justify-center rounded-full border border-border-tertiary bg-bg-container',
                'text-text-tertiary transition-opacity duration-100 hover:text-text focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-border',
                isHovered ? 'opacity-100' : 'opacity-0'
              )}
            >
              <CloseLine className="size-3" />
            </button>
          )}
        </div>
      )

      if (tooltipContent) {
        return (
          <Tooltip content={tooltipContent} side="top">
            {attachment}
          </Tooltip>
        )
      }

      return attachment
    }

    return (
      <div
        ref={ref}
        {...props}
        role={interactive ? 'button' : undefined}
        tabIndex={interactive ? 0 : undefined}
        aria-disabled={deleted || undefined}
        onClick={interactive ? onClick : undefined}
        onKeyDown={handleKeyDown}
        onMouseEnter={onMouseEnter}
        onMouseLeave={onMouseLeave}
        className={cn(
          'group/file-card relative flex h-16 w-full max-w-96 items-center overflow-hidden rounded-lg border border-border-tertiary/60 bg-bg-base pr-4 text-sm leading-sm',
          'transition-colors duration-200',
          interactive && 'cursor-pointer hover:border-border-secondary',
          !interactive && 'cursor-default',
          deleted && 'border-error-border/60 opacity-60',
          className
        )}
      >
        {content}
      </div>
    )
  }
)

FileCard.displayName = 'FileCard'
