import { WebApplicationGroup } from '@/Application/WebApplicationGroup'
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
import { useCallback, useEffect, useState } from 'react'
import { AccountMenuPane } from '../AccountMenu/AccountMenuPane'
import MenuPaneSelector from '../AccountMenu/MenuPaneSelector'
import { useApplication } from '../ApplicationProvider'
import Icon from '../Icon/Icon'
import Menu from '../Menu/Menu'
import MenuItem from '../Menu/MenuItem'
import { storage as extensionStorage, windows } from 'webextension-polyfill'
import sendMessageToActiveTab from '@standardnotes/clipper/src/utils/sendMessageToActiveTab'
import { ClipPayload, RuntimeMessageTypes } from '@standardnotes/clipper/src/types/message'
import { confirmDialog } from '@standardnotes/ui-services'
import {
  ApplicationEvent,
  ContentType,
  DecryptedItem,
  FeatureIdentifier,
  FeatureStatus,
  NoteContent,
  NoteType,
  PrefKey,
  SNNote,
  SNTag,
} from '@standardnotes/snjs'
import { addToast, ToastType } from '@standardnotes/toast'
import { getSuperJSONFromClipPayload } from './getSuperJSONFromClipHTML'
import ClippedNoteView from './ClippedNoteView'
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '../Icon/PremiumFeatureIcon'
import Button from '../Button/Button'
import { openSubscriptionDashboard } from '@/Utils/ManageSubscription'
import { useStateRef } from '@/Hooks/useStateRef'
import usePreference from '@/Hooks/usePreference'
import { createLinkFromItem } from '@/Utils/Items/Search/createLinkFromItem'
import ItemSelectionDropdown from '../ItemSelectionDropdown/ItemSelectionDropdown'
import LinkedItemBubble from '../LinkedItems/LinkedItemBubble'
import StyledTooltip from '../StyledTooltip/StyledTooltip'
import MenuSwitchButtonItem from '../Menu/MenuSwitchButtonItem'

const ClipperView = ({
  viewControllerManager,
  applicationGroup,
}: {
  viewControllerManager: ViewControllerManager
  applicationGroup: WebApplicationGroup
}) => {
  const application = useApplication()

  const [currentWindow, setCurrentWindow] = useState<Awaited<ReturnType<typeof windows.getCurrent>>>()
  useEffect(() => {
    windows
      .getCurrent({
        populate: true,
      })
      .then((window) => {
        setCurrentWindow(window)
      })
      .catch(console.error)
  }, [])
  const isFirefoxPopup = !!currentWindow && currentWindow.type === 'popup' && currentWindow.incognito === false

  const [user, setUser] = useState(() => application.getUser())
  const [isEntitledToExtension, setIsEntitled] = useState(
    () => application.features.getFeatureStatus(FeatureIdentifier.Extension) === FeatureStatus.Entitled,
  )
  const isEntitledRef = useStateRef(isEntitledToExtension)
  const hasSubscription = application.hasValidFirstPartySubscription()
  useEffect(() => {
    return application.addEventObserver(async (event) => {
      switch (event) {
        case ApplicationEvent.SignedIn:
        case ApplicationEvent.SignedOut:
        case ApplicationEvent.UserRolesChanged:
          setUser(application.getUser())
          setIsEntitled(application.features.getFeatureStatus(FeatureIdentifier.Extension) === FeatureStatus.Entitled)
          break
        case ApplicationEvent.FeaturesAvailabilityChanged:
          setIsEntitled(application.features.getFeatureStatus(FeatureIdentifier.Extension) === FeatureStatus.Entitled)
          break
      }
    })
  }, [application])

  const defaultTagId = usePreference(PrefKey.ClipperDefaultTagUuid)
  const [defaultTag, setDefaultTag] = useState<SNTag | undefined>()
  const defaultTagRef = useStateRef(defaultTag)

  useEffect(() => {
    if (!defaultTagId) {
      setDefaultTag(undefined)
      return
    }

    const tag = application.items.findItem(defaultTagId) as SNTag | undefined
    setDefaultTag(tag)
  }, [defaultTagId, application])

  const selectTag = useCallback(
    (tag: DecryptedItem) => {
      void application.setPreference(PrefKey.ClipperDefaultTagUuid, tag.uuid)
    },
    [application],
  )

  const unselectTag = useCallback(async () => {
    void application.setPreference(PrefKey.ClipperDefaultTagUuid, undefined)
  }, [application])

  const [menuPane, setMenuPane] = useState<AccountMenuPane>()

  const activateRegisterPane = useCallback(() => {
    setMenuPane(AccountMenuPane.Register)
  }, [setMenuPane])

  const activateSignInPane = useCallback(() => {
    setMenuPane(AccountMenuPane.SignIn)
  }, [setMenuPane])

  const showSignOutConfirmation = useCallback(async () => {
    if (
      await confirmDialog({
        title: 'Sign Out',
        text: 'Are you sure you want to sign out?',
        confirmButtonText: 'Sign Out',
        confirmButtonStyle: 'danger',
        cancelButtonText: 'Cancel',
      })
    ) {
      await application.user.signOut()
    }
  }, [application.user])

  const [isScreenshotMode, setIsScreenshotMode] = useState(false)
  useEffect(() => {
    void sendMessageToActiveTab({
      type: RuntimeMessageTypes.ToggleScreenshotMode,
      enabled: isScreenshotMode,
    })
  }, [isScreenshotMode])

  const [hasSelection, setHasSelection] = useState(false)
  useEffect(() => {
    if (!user) {
      return
    }

    try {
      const checkIfPageHasSelection = async () => {
        setHasSelection(Boolean(await sendMessageToActiveTab({ type: RuntimeMessageTypes.HasSelection })))
      }

      void checkIfPageHasSelection()
    } catch (error) {
      console.error(error)
    }
  }, [user])

  const [clipPayload, setClipPayload] = useState<ClipPayload>()
  useEffect(() => {
    const getClipFromStorage = async () => {
      const result = await extensionStorage.local.get('clip')
      if (!result.clip) {
        return
      }
      setClipPayload(result.clip)
      void extensionStorage.local.remove('clip')
    }

    void getClipFromStorage()
  }, [])

  const clearClip = useCallback(() => {
    setClipPayload(undefined)
  }, [])

  const [clippedNote, setClippedNote] = useState<SNNote>()
  useEffect(() => {
    if (!isEntitledRef.current) {
      return
    }

    async function createNoteFromClip() {
      if (!clipPayload) {
        setClippedNote(undefined)
        return
      }
      if (!clipPayload.content) {
        addToast({
          type: ToastType.Error,
          message: 'No content to clip',
        })
        return
      }
      if (clipPayload.isScreenshot) {
        const blob = await fetch(clipPayload.content).then((response) => response.blob())

        const file = new File([blob], `${clipPayload.title} - ${clipPayload.url}.png`, {
          type: 'image/png',
        })

        const uploadedFile = await viewControllerManager.filesController.uploadNewFile(file).catch(console.error)

        if (uploadedFile && defaultTagRef.current) {
          await application.linkingController.linkItems(uploadedFile, defaultTagRef.current)
        }

        return
      }

      const editorStateJSON = await getSuperJSONFromClipPayload(clipPayload)

      const note = application.items.createTemplateItem<NoteContent, SNNote>(ContentType.TYPES.Note, {
        title: clipPayload.title,
        text: editorStateJSON,
        editorIdentifier: FeatureIdentifier.SuperEditor,
        noteType: NoteType.Super,
        references: [],
      })

      const insertedNote = await application.mutator.insertItem(note)

      if (defaultTagRef.current) {
        await application.linkingController.linkItems(insertedNote, defaultTagRef.current)
      }

      setClippedNote(insertedNote as SNNote)

      addToast({
        type: ToastType.Success,
        message: 'Note clipped successfully',
      })

      void application.sync.sync()
    }

    void createNoteFromClip()
  }, [
    application.items,
    application.linkingController,
    application.mutator,
    application.sync,
    clipPayload,
    defaultTagRef,
    isEntitledRef,
    viewControllerManager.filesController,
  ])

  const upgradePlan = useCallback(async () => {
    if (hasSubscription) {
      await openSubscriptionDashboard(application)
    } else {
      await application.openPurchaseFlow()
    }
    window.close()
  }, [application, hasSubscription])

  if (user && !isEntitledToExtension) {
    return (
      <div className="px-3 py-3">
        <div
          className="mx-auto mb-5 flex h-24 w-24 items-center justify-center rounded-[50%] bg-contrast"
          aria-hidden={true}
        >
          <Icon className={`h-12 w-12 ${PremiumFeatureIconClass}`} size={'custom'} type={PremiumFeatureIconName} />
        </div>
        <div className="mb-1 text-center text-lg font-bold">Enable Advanced Features</div>
        <div className="mb-3 text-center">
          To take advantage of <span className="font-semibold">Web Clipper</span> and other advanced features, upgrade
          your current plan.
        </div>
        <Button className="mb-2" fullWidth primary onClick={upgradePlan}>
          Upgrade
        </Button>
        <Button fullWidth onClick={showSignOutConfirmation}>
          Sign out
        </Button>
      </div>
    )
  }

  if (clippedNote) {
    return (
      <ClippedNoteView
        note={clippedNote}
        key={clippedNote.uuid}
        linkingController={viewControllerManager.linkingController}
        clearClip={clearClip}
        isFirefoxPopup={isFirefoxPopup}
      />
    )
  }

  if (!user) {
    return menuPane ? (
      <div className="py-1">
        <MenuPaneSelector
          viewControllerManager={viewControllerManager}
          application={application}
          mainApplicationGroup={applicationGroup}
          menuPane={menuPane}
          setMenuPane={setMenuPane}
          closeMenu={() => setMenuPane(undefined)}
        />
      </div>
    ) : (
      <Menu a11yLabel="User account menu" isOpen={true}>
        <MenuItem onClick={activateRegisterPane}>
          <Icon type="user" className="mr-2 h-6 w-6 text-neutral md:h-5 md:w-5" />
          Create free account
        </MenuItem>
        <MenuItem onClick={activateSignInPane}>
          <Icon type="signIn" className="mr-2 h-6 w-6 text-neutral md:h-5 md:w-5" />
          Sign in
        </MenuItem>
      </Menu>
    )
  }

  return (
    <div className="bg-contrast p-3">
      <Menu a11yLabel="Extension menu" isOpen={true} className="rounded border border-border bg-default">
        {hasSelection && (
          <MenuItem
            className="border-b border-border"
            disabled={isScreenshotMode}
            onClick={async () => {
              const payload = await sendMessageToActiveTab({ type: RuntimeMessageTypes.GetSelection })
              if (!payload) {
                return
              }
              setClipPayload(payload)
            }}
          >
            <Icon type="paragraph" className="mr-2 text-info" />
            Clip text selection
          </MenuItem>
        )}
        <MenuItem
          onClick={async () => {
            const payload = await sendMessageToActiveTab({ type: RuntimeMessageTypes.GetFullPage })
            if (!payload) {
              return
            }
            setClipPayload(payload)
          }}
        >
          <Icon type="notes-filled" className="mr-2 text-info" />
          {isScreenshotMode ? 'Capture visible' : 'Clip full page'}
        </MenuItem>
        <MenuItem
          disabled={isScreenshotMode}
          onClick={async () => {
            const payload = await sendMessageToActiveTab({ type: RuntimeMessageTypes.GetArticle })
            if (!payload) {
              return
            }
            setClipPayload(payload)
          }}
        >
          <Icon type="rich-text" className="mr-2 text-info" />
          Clip article
        </MenuItem>
        <MenuItem
          onClick={async () => {
            void sendMessageToActiveTab({ type: RuntimeMessageTypes.StartNodeSelection })
            window.close()
          }}
        >
          <Icon type="dashboard" className="mr-2 text-info" />
          Select elements to {isScreenshotMode ? 'capture' : 'clip'}
        </MenuItem>
        <MenuSwitchButtonItem
          checked={isScreenshotMode}
          onChange={function (checked: boolean): void {
            setIsScreenshotMode(checked)
          }}
          className="flex-row-reverse gap-2"
          forceDesktopStyle={true}
        >
          Clip as screenshot
        </MenuSwitchButtonItem>
        <div className="border-t border-border px-3 py-3  text-foreground">
          {defaultTag && (
            <div className="flex items-center justify-between text-base">
              <LinkedItemBubble
                className="m-1 mr-2 min-w-0"
                link={createLinkFromItem(defaultTag, 'linked')}
                unlinkItem={unselectTag}
                isBidirectional={false}
              />
              <StyledTooltip label="Remove default tag" gutter={2}>
                <button
                  className="rounded-full p-1 text-neutral hover:bg-contrast hover:text-info"
                  onClick={unselectTag}
                >
                  <Icon type="clear-circle-filled" />
                </button>
              </StyledTooltip>
            </div>
          )}
          <ItemSelectionDropdown
            onSelection={selectTag}
            placeholder="Select tag to save clipped notes to..."
            contentTypes={[ContentType.TYPES.Tag]}
            className={{
              input: 'text-[0.85rem]',
            }}
            comboboxProps={{
              placement: 'top',
            }}
          />
        </div>
        <div className="flex items-center border-t border-border text-foreground">
          <Icon type="user" className="mx-2" />
          <div className="flex-grow py-2 text-sm font-semibold">{user.email}</div>
          <button
            className="flex-shrink-0 border-l border-border px-2 py-2 hover:bg-info-backdrop focus:bg-info-backdrop focus:shadow-none focus:outline-none"
            onClick={showSignOutConfirmation}
          >
            <Icon type="signOut" className="text-neutral" />
          </button>
        </div>
      </Menu>
    </div>
  )
}

export default ClipperView
