import styled from "@emotion/styled"
import { useEffect, useMemo, useState } from "react"
import type { Vault, Item } from "opvault.js"
import { Category } from "opvault.js"
import { FiLock } from "react-icons/fi"
import { IoSearch } from "react-icons/io5"
import { Si1Password } from "react-icons/si"
import { BsGear } from "react-icons/bs"
import { ItemList } from "../components/ItemList"
import { ItemView } from "../components/Item"
import { reactIconClass } from "../components/CategoryIcon"
import { useTranslate } from "../i18n/index"
import { scrollbar } from "../styles"
import { Settings } from "../settings"

const Container = styled.div`
  display: flex;
  height: calc(100vh - var(--titlebar-height));
`
const TabContainer = styled.div`
  border-right: 1px solid var(--border-color);
  display: flex;
  flex-direction: column;
  width: 55px;
  overflow: hidden;
  padding-bottom: 5px;
  @media (prefers-color-scheme: dark) {
    background: #222;
    border-right-color: transparent;
  }
  &&::-webkit-scrollbar {
    display: none;
  }
`
const TabButton = styled.button<{ active?: boolean }>`
  align-items: center;
  background: ${p => (p.active ? "var(--selected-background)" : "transparent")};
  border-radius: ${p => (p.active ? 0 : 3)}px;
  border: transparent;
  box-shadow: none;
  display: inline-flex;
  margin-bottom: 5px;
  font-size: 22px;
  padding: 10px 14px;
  @media (prefers-color-scheme: dark) {
    --selected-background: #1c1c1c;
  }
`
const TabContainerMain = styled.div`
  flex-grow: 1;
`
const ListContainer = styled.div`
  border-right: 1px solid var(--border-color);
  width: 350px;
  margin-right: 10px;
  overflow-y: scroll;
  overflow-x: hidden;
  @media (prefers-color-scheme: dark) {
    background: #202020;
    border-right-color: transparent;
  }
`
const ItemContainer = styled.div`
  width: calc(100% - 300px);
  overflow: hidden;
`
const SearchContainer = styled.div`
  text-align: center;
  position: relative;
  flex-grow: 1;
  margin: 10px 0;
  margin-right: 10px;
`
const SortContainer = styled.div`
  margin: 10px 10px;
`

const SearchInput = styled.input`
  --margin: 10px;
  width: calc(100% - var(--margin) * 2 + 9px);
  margin: 0 var(--margin);
  padding-left: 2em !important;
`
const SearchIcon = styled(IoSearch)`
  position: absolute;
  top: 9px;
  left: 20px;
`

const enum SortBy {
  Name,
  CreatedAt,
  UpdatedAt,
}

export const VaultView: React.FC<{ vault: Vault; onLock(): void }> = ({
  vault,
  onLock,
}) => {
  const [showSettings, setShowSettings] = useState(false)
  const t = useTranslate()
  const [items, setItems] = useState<Item[]>(() => [])
  const [item, setItem] = useState<Item>()
  const [sortBy, setSortBy] = useState(SortBy.Name)
  const [search, setSearch] = useState("")

  const compareFn = useMemo((): ((a: Item, b: Item) => number) => {
    switch (sortBy) {
      case SortBy.Name:
        return (a, b) => (a.overview.title ?? "").localeCompare(b?.overview.title ?? "")
      case SortBy.CreatedAt:
        return (a, b) => b.createdAt - a.createdAt
      case SortBy.UpdatedAt:
        return (a, b) => b.updatedAt - a.updatedAt
    }
  }, [sortBy])

  const sortedItem = useMemo(() => items.slice().sort(compareFn), [items, compareFn])

  useEffect(() => {
    setItem(undefined)
    arrayFrom(vault.values()).then(setItems)
  }, [vault])

  const filtered = useMemo(() => {
    const items = sortedItem.filter(x => x.category !== Category.Tombstone)
    let res: Item[] = items
    if (search) {
      res = []
      for (const x of items) {
        const compare = Math.max(
          stringCompare(search, x.overview.title),
          stringCompare(search, x.overview.ainfo)
        ) as CompareResult
        switch (compare) {
          case CompareResult.NoMatch:
            continue
          case CompareResult.Includes:
            res.push(x)
            break
          case CompareResult.Equals:
            res.unshift(x)
            break
        }
      }
    }
    return res
  }, [sortedItem, search])

  return (
    <Container>
      <TabContainer>
        <TabContainerMain>
          <TabButton active>
            <Si1Password />
          </TabButton>
        </TabContainerMain>
        <TabButton onClick={onLock} title={t.action.lock}>
          <FiLock />
        </TabButton>
        <TabButton onClick={() => setShowSettings(true)} title={t.label.settings}>
          <BsGear />
        </TabButton>
      </TabContainer>

      <ListContainer className={scrollbar}>
        <SearchContainer>
          <SearchInput
            type="search"
            value={search}
            onChange={e => setSearch(e.currentTarget.value)}
          />
          <SearchIcon className={reactIconClass} />
        </SearchContainer>

        <SortContainer>
          <select
            style={{ width: "100%" }}
            value={sortBy}
            onChange={e => setSortBy(+e.currentTarget.value)}
          >
            <option value={SortBy.Name}>{t.options.sort_by_name}</option>
            <option value={SortBy.CreatedAt}>{t.options.sort_by_created_at}</option>
            <option value={SortBy.UpdatedAt}>{t.options.sort_by_updated_at}</option>
          </select>
        </SortContainer>
        <ItemList items={filtered} onSelect={setItem} selected={item} />
      </ListContainer>
      <ItemContainer>
        {item && <ItemView className={scrollbar} item={item} />}
      </ItemContainer>

      <Settings show={showSettings} onHide={() => setShowSettings(false)} />
    </Container>
  )
}

async function arrayFrom<T>(generator: AsyncGenerator<T, void, unknown>) {
  const list: T[] = []
  for await (const value of generator) {
    list.push(value)
  }
  return list
}

enum CompareResult {
  NoMatch,
  Includes,
  Equals,
}

function stringCompare(search: string, source?: string) {
  if (!search) return CompareResult.Includes
  if (!source) return CompareResult.NoMatch
  source = source.toLocaleLowerCase()
  search = search.toLocaleUpperCase()
  const includes = source.includes(search.toLocaleLowerCase())
  if (includes) {
    return source.length === search.length ? CompareResult.Equals : CompareResult.Includes
  }
  return CompareResult.NoMatch
}
