// Dependencies
import {
  about,
  collection,
  tags,
  techniques,
  designers,
  developers,
  examples,
  measurements,
  requiredMeasurements,
  optionalMeasurements,
} from '@freesewing/collection'
import { capitalize, linkClasses, mutateObject } from '@freesewing/utils'
import { measurements as measurementsTranslations } from '@freesewing/i18n'

// Context
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
import { ModalContext } from '@freesewing/react/context/Modal'

// Hooks
import React, { useState, useContext, Fragment } from 'react'
import { useFilter } from '@freesewing/react/hooks/useFilter'

// Components
import { Link as WebLink, AnchorLink } from '@freesewing/react/components/Link'
import {
  CircleIcon,
  CisFemaleIcon,
  DocsIcon,
  FilterIcon,
  HeartIcon,
  NewPatternIcon,
  ResetIcon,
  ShowcaseIcon,
} from '@freesewing/react/components/Icon'
import {
  lineDrawingsFront as lineDrawings,
  lineDrawingsBack,
} from '@freesewing/react/components/LineDrawing'
import { IconButton } from '@freesewing/react/components/Button'
import { ModalWrapper } from '@freesewing/react/components/Modal'
import { KeyVal } from '@freesewing/react/components/KeyVal'
import { MissingLinedrawing } from '../LineDrawing/missing.mjs'

/**
 * A component to show the FreeSewing collection and pick a design.
 *
 * @component
 * @param {object} props - All component props
 * @param {React.Component} [props.Link = false] - A framework specific Link component for client-side routing
 * @param {boolean} [props.editor = false] - Set this to true when rendering inside the FreeSewing editor
 * @param {string} [props.linkTo = 'about'] - This controls where to link the design to. One of 'new', 'docs', or 'about'.
 * @param {functino} [props.onClick = false] - You can pass in an onClick handler rather than using links
 * @returns {JSX.Element}
 */
export const Collection = ({ Link = false, linkTo = 'about', editor = false, onClick = false }) => {
  if (!Link) Link = WebLink

  // State
  const [filter, setFilter] = useFilter()
  const [showFilters, setShowFilters] = useState(false)

  /*
   * Apply filter
   */
  const filtered = {}
  for (const d of collection) {
    let skip = false
    if (
      filter.tag &&
      filter.tag.filter((tag) => about[d].tags.includes(tag)).length < filter.tag.length
    )
      skip = true
    if (
      filter.tech &&
      filter.tech.filter((tech) => about[d].techniques.includes(tech)).length < filter.tech.length
    )
      skip = true
    if (filter.difficulty && filter.difficulty !== about[d].difficulty) skip = true
    if (!skip) filtered[d] = d
  }

  const updateFilter = (path, val) => {
    // Allow clicking the same difficulty to remove it as a filter
    if (path === 'difficulty' && val === filter.difficulty) val = 'unset'
    const newFilter = mutateObject({ ...filter }, path, val)
    setFilter(newFilter)
  }

  const toggle = (type, val) => {
    const current = filter[type] || []
    const newSet = new Set(current)
    if (newSet.has(val)) newSet.delete(val)
    else newSet.add(val)
    updateFilter(type, [...newSet])
  }

  return (
    <>
      <div className="tw:max-w-7xl tw:m-auto" data-component="Collection/Collection">
        <div className="tw:flex tw:flex-row tw:flex-wrap tw:gap-1 tw:justify-center tw:font-medium tw:mb-2">
          {Object.keys(filtered)
            .sort()
            .map((d) =>
              onClick ? (
                <button
                  key={d}
                  onClick={() => onClick(d)}
                  className="tw:text-secondary tw:decoration-2 tw:underline tw:capitalize tw:hover:decoration-4 tw:hover:text-secondary tw:bg-transparent tw:border-0 tw:font-medium tw:p-0 tw:text-base tw:hover:cursor-pointer"
                >
                  {d}
                </button>
              ) : (
                <Link
                  key={d}
                  href={linkBuilders[linkTo](d)}
                  className="tw:text-secondary tw:decoration-2 tw:underline tw:capitalize tw:hover:decoration-4 tw:hover:text-secondary"
                >
                  {d}
                </Link>
              )
            )}
        </div>
        {showFilters ? (
          <>
            <h6 className="tw:text-center tw:mb-0 tw:mt-4">
              Filtered Designs ({Object.keys(filtered).length}/{collection.length})
            </h6>
            <div className="tw:flex tw:flex-row tw:gap-1 tw:items-center tw:justify-center tw:flex-wrap tw:my-2">
              <b>Tags:</b>
              {tags.map((tag) => (
                <button
                  key={tag}
                  className={`tw:daisy-badge tw:font-medium tw:hover:shadow tw:hover:cursor-pointer
                  ${
                    filter?.tag && Array.isArray(filter.tag) && filter.tag.includes(tag)
                      ? 'tw:daisy-badge-success hover:tw:daisy-badge-error'
                      : 'tw:daisy-badge-primary hover:tw:daisy-badge-success'
                  }`}
                  onClick={() => toggle('tag', tag)}
                >
                  {tag}
                </button>
              ))}
            </div>
            <div className="tw:flex tw:flex-row tw:gap-1 tw:items-center tw:justify-center tw:flex-wrap tw:my-4">
              <b>Techniques</b>
              {techniques.sort().map((tech) => (
                <button
                  key={tech}
                  className={`tw:daisy-badge tw:font-medium tw:hover:shadow
                 ${
                   filter?.tech && Array.isArray(filter.tech) && filter.tech.includes(tech)
                     ? 'tw:daisy-badge tw:daisy-badge-success hover:tw:daisy-badge-error'
                     : 'tw:daisy-badge tw:daisy-badge-accent hover:tw:daisy-badge-success'
                 }`}
                  onClick={() => toggle('tech', tech)}
                >
                  {tech}
                </button>
              ))}
            </div>
            <div className="tw:flex tw:flex-row tw:gap-2 tw:items-center tw:justify-center tw:flex-wrap tw:my-4">
              <b>Difficulty:</b>
              {[1, 2, 3, 4, 5].map((score) => (
                <button
                  onClick={() => updateFilter('difficulty', score)}
                  key={score}
                  className={`tw:daisy-btn tw:daisy-btn-sm ${
                    filter.difficulty === score
                      ? 'tw:daisy-btn-secondary tw:daisy-btn-outline'
                      : 'tw:daisy-btn-ghost'
                  }`}
                >
                  <Difficulty score={score} />
                </button>
              ))}
            </div>
            <div className="tw:flex tw:flex-row tw:gap-4 tw:items-center tw:justify-center tw:flex-wrap tw:my-2">
              <button
                className="tw:daisy-btn tw:daisy-btn-secondary tw:daisy-btn-outline"
                onClick={() => updateFilter('ld', !filter.ld)}
              >
                {filter.ld ? <CisFemaleIcon /> : <ShowcaseIcon />}
                {filter.ld ? 'Show Examples' : 'Show Line Drawings'}
              </button>
              <button
                className="tw:daisy-btn tw:daisy-btn-secondary tw:daisy-btn-outline"
                onClick={() => setFilter({ ld: 1 })}
              >
                <ResetIcon />
                Clear Filter
              </button>
              <button
                className="tw:daisy-btn tw:daisy-btn-secondary tw:daisy-btn-outline"
                onClick={() => setShowFilters(false)}
              >
                <FilterIcon />
                Hide Filters
              </button>
            </div>
          </>
        ) : (
          <div className="tw:flex tw:flex-row tw:gap-4 tw:items-center tw:justify-center tw:flex-wrap tw:my-2">
            <button
              className="tw:daisy-btn tw:daisy-btn-secondary tw:daisy-btn-outline"
              onClick={() => updateFilter('ld', !filter.ld)}
            >
              {filter.ld ? <ShowcaseIcon /> : <CisFemaleIcon />}
              {filter.ld ? 'Show Examples' : 'Show Line Drawings'}
            </button>
            <button
              className="tw:daisy-btn tw:daisy-btn-secondary tw:daisy-btn-outline"
              onClick={() => setShowFilters(true)}
            >
              <FilterIcon />
              Show Filters
            </button>
          </div>
        )}
      </div>
      <div
        className={`tw:grid tw:grid-cols-2 tw:gap-2 tw:mt-4 tw:justify-center tw:sm:grid-cols-3 tw:md:grid-cols-4 ${editor ? 'tw:lg:grid-cols-6 tw:2xl:grid-cols-12' : ''} tw:mb-8`}
      >
        {Object.keys(filtered)
          .sort()
          .map((d) => (
            <DesignCard
              name={d}
              key={d}
              linkTo={linkTo}
              onClick={onClick}
              lineDrawing={filter.ld ? true : false}
            />
          ))}
      </div>
    </>
  )
}

/*
 * A helper component to show a design technique
 *
 * @param {object} props - All React props
 * @param {function} props.Link - A Link component, typically specific to the framework for client-side routing
 * @param {string} props.technique - The technique name/id
 */
const Technique = ({ Link = WebLink, technique }) => (
  <Link
    href={`/designs/techniques/${technique}`}
    className="tw:daisy-badge tw:daisy-badge-accent hover:tw:daisy-badge-secondary tw:hover:shadow tw:font-medium"
  >
    {technique}
  </Link>
)

/*
 * A helper component to show a design tag
 *
 * @param {object} props - All React props
 * @param {function} props.Link - A Link component, typically specific to the framework for client-side routing
 * @param {string} props.tag - The tag name/id
 */
const Tag = ({ Link = WebLink, technique }) => (
  <Link
    href={`/designs/tags/${tag}`}
    className="tw:daisy-badge tw:daisy-badge-primary hover:tw:daisy-badge-secondary tw:hover:shadow tw:font-medium"
  >
    {tag}
  </Link>
)

const DesignCard = ({ name, lineDrawing = false, linkTo, Link, onClick }) => {
  if (!Link) Link = WebLink

  const LineDrawing = lineDrawing && lineDrawings[name] ? lineDrawings[name] : MissingLinedrawing
  const exampleImageUrl = examples.href[name] ? examples.href[name] : noExample
  const bg = { aspectRatio: '1/1.4' }
  if (!lineDrawing) {
    bg.backgroundImage = `url(${exampleImageUrl}`
    bg.backgroundSize = 'cover'
    bg.backgroundPosition = 'center center'
  }

  const inner = (
    <div
      className={`tw:flex tw:flex-col tw:flex-nowrap tw:items-start tw:justify-between tw:gap-2 tw:border-neutral-500 tw:group-hover:border-secondary
      tw:w-full tw:h-full tw:border tw:border-2 tw:border-solid tw:p-0 tw:relative tw:rounded-lg tw:rounded-lg`}
      style={bg}
    >
      <h5
        className={`tw:text-center tw:py-2 tw:px-4 tw:rounded-t tw:m-0 tw:w-full tw:group-hover:no-underline tw:group-hover:bg-secondary/80 tw:group-hover:text-secondary-content
      ${lineDrawing ? '' : 'tw:bg-neutral/80'}`}
      >
        <span className={lineDrawing ? 'tw:text-base-100-content' : 'tw:text-neutral-content'}>
          {about[name].name}
        </span>
      </h5>
      {lineDrawing ? (
        <div className="tw:grow tw:w-full tw:h-auto tw:square tw:text-center">
          <LineDrawing className="tw:w-5/6 tw:m-auto tw:my-0 tw:text-base-content" />
        </div>
      ) : (
        <span />
      )}
      <div
        className={`tw:flex tw:flex-row tw:items-center tw:justify-center tw:py-1 tw:px-2 tw:rounded-b tw:m-0 tw:w-full
      ${lineDrawing ? '' : `tw:text-neutral-content`}`}
      >
        <Difficulty score={about[name].difficulty} className="tw:group-hover:text-secondary" />
      </div>
    </div>
  )

  return onClick ? (
    <button
      onClick={() => onClick(name)}
      className="tw:hover:bg-secondary/10 tw:rounded-lg tw:group tw:hover:no-underline tw:bg-transparent tw:border-0 tw:hover:cursor-pointer tw:p-0"
      title={about[name].description}
      data-component="Collection/DesignCard"
    >
      {inner}
    </button>
  ) : (
    <Link
      href={linkBuilders[linkTo](name)}
      className="tw:hover:bg-secondary/10 tw:rounded-lg tw:group tw:hover:no-underline"
      title={about[name].description}
      data-component="Collection/DesignCard"
    >
      {inner}
    </Link>
  )
}

/*
 * A helper component to show difficulety of a design
 *
 * @param {object} props - All React props
 * @param {number} props.score - The difficulty score of the design (1-5)
 */
const Difficulty = ({ score = 0, className = '' }) => (
  <div className={`tw:flex tw:flex-row tw:items-center ${className}`}>
    {[0, 1, 2, 3, 4].map((i) => (
      <CircleIcon key={i} fill={i < score ? true : false} className={`tw:w-4 tw:h-4`} />
    ))}
  </div>
)

const linkBuilders = {
  new: (design) =>
    `/editor/#s={%22design%22%3A%22${design.toLowerCase()}%22%2C%22view%22%3A%22draft%22}`,
  docs: (design) => `/docs/designs/${design.toLowerCase()}/`,
  about: (design) => `/designs/${design.toLowerCase()}/`,
}

const noExample =
  'https://images.pexels.com/photos/5626595/pexels-photo-5626595.jpeg?cs=srgb&fm=jpg&w=640&h=427'

/**
 * A component to show info about a FreeSewing design
 *
 * @component
 * @param {object} props - All component props
 * @param {React.Component} props.Link - A framework specific Link component for client-side routing
 * @param {string} props.design - The name/id of the design
 * @param {boolean} props.noDocsLink - Set this to true to not render a link to the documentation
 * @returns {JSX.Element}
 */
export const DesignInfo = ({ Link = false, design = false, noDocsLink = false }) => {
  if (!Link) Link = WebLink

  // State
  const [back, setBack] = useState(false)

  // Context
  const { setModal, clearModal } = useContext(ModalContext)
  const { setLoadingStatus } = useContext(LoadingStatusContext)

  if (!design) return null

  // Line drawings
  const LineDrawing = lineDrawings[design] || MissingLinedrawing
  const LineDrawingBack = lineDrawingsBack[design] || null

  // Make sure these always hold arrays, that way we can just map() over them in the JSX output
  const codeBy = Array.isArray(about[design].code) ? about[design].code : [about[design].code]
  const designBy = Array.isArray(about[design].design)
    ? about[design].design
    : [about[design].design]
  const tags = about[design].tags || []
  const techniques = about[design].techniques || []
  const colors = {
    1: 'success',
    2: 'success',
    3: 'warning',
    4: 'warning',
    5: 'error',
  }

  const makeButton = (
    <div className={`tw:grid tw:grid-cols-1 tw:gap-2 tw:mb-4`}>
      <IconButton href={`/editor/#s={"design"%3A"${design}"%2C"view"%3A"draft"}`} color="primary">
        <NewPatternIcon className="tw:w-8 tw:h-8" />
        New {capitalize(design)} pattern
      </IconButton>
    </div>
  )
  const buttons = noDocsLink ? (
    makeButton
  ) : (
    <div className={`tw:grid tw:grid-cols-1 tw:lg:grid-cols-2 tw:gap-2 tw:mb-4`}>
      <IconButton href={`/docs/designs/${design}`} color="secondary">
        <DocsIcon className="tw:w-8 tw:h-8" />
        Documentation
      </IconButton>
      {makeButton}
    </div>
  )

  return (
    <>
      <div className="tw:lg:hidden">{buttons}</div>
      <div className={`tw:grid tw:grid-cols-1 tw:lg:grid-cols-2 tw:gap-2`}>
        <div className="tw:relative">
          <div className="tw tw:top-0 tw:left-0">
            {back ? <LineDrawingBack /> : <LineDrawing />}
          </div>
          {LineDrawingBack ? (
            <button
              className="tw:absolute tw:top-2 tw:right-4 tw:start-auto tw:daisy-btn tw:daisy-btn-neutral tw:daisy-btn-outline tw:daisy-btn-xs"
              onClick={() => setBack(!back)}
            >
              {back ? 'Front' : 'Back'} view
            </button>
          ) : null}
        </div>
        <div className="">
          <div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">Description</div>
          <span className="tw:text-xl">{about[design].description}</span>

          <div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">By</div>
          <div className="tw:flex tw:flex-row tw:flex-wrap tw:gap-1 items-center">
            {codeBy.map((code) => (
              <KeyVal key={code} k="code" val={code} color="secondary" />
            ))}
            {designBy.map((code) => (
              <KeyVal key={code} k="design" val={code} color="secondary" />
            ))}
          </div>

          <div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">Difficulty</div>
          <Difficulty score={about[design].difficulty} />

          {optionalMeasurements[design].length > 0 ? (
            <>
              <div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">
                Optional Measurements
              </div>
              <div className="">
                {optionalMeasurements[design].map((m, i) => (
                  <Fragment key={m}>
                    <Link
                      href={`/docs/measurements/${m.toLowerCase()}`}
                      key={m}
                      className={linkClasses}
                    >
                      {measurementsTranslations[m]}
                    </Link>
                    {i < optionalMeasurements[design].length - 1 ? <span>, </span> : null}
                  </Fragment>
                ))}
              </div>
            </>
          ) : null}

          {requiredMeasurements[design].length > 0 ? (
            <>
              <div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">
                Required Measurements
              </div>
              <div className="">
                {requiredMeasurements[design].map((m, i) => (
                  <Fragment key={m}>
                    <Link
                      href={`/docs/measurements/${m.toLowerCase()}`}
                      key={m}
                      className={linkClasses}
                    >
                      {measurementsTranslations[m]}
                    </Link>
                    {i < requiredMeasurements[design].length - 1 ? <span>, </span> : null}
                  </Fragment>
                ))}
              </div>
            </>
          ) : null}

          <div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">Tags</div>
          <div className="tw:flex tw:flex-row tw:flex-wrap tw:gap-1 items-center">
            {tags.map((tag) => (
              <Link
                key={tag}
                className="tw:daisy-badge tw:daisy-badge-primary tw:font-medium tw:hover:shadow tw:hover:cursor-pointer"
                href={`/designs/#filter={"example"%3Atrue%2C"tag"%3A["${tag}"]}`}
              >
                <span className="tw:text-primary-content">{tag}</span>
              </Link>
            ))}
          </div>
          <div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">Techniques</div>
          <div className="tw:flex tw:flex-row tw:flex-wrap tw:gap-1 items-center">
            {techniques.map((tech) => (
              <Link
                key={tech}
                className="tw:daisy-badge tw:daisy-badge-accent tw:font-medium tw:hover:shadow tw:hover:cursor-pointer"
                href={`/designs/#filter={"example"%3Atrue%2C"tag"%3A["${tech}"]}`}
              >
                <span className="tw:text-accent-content">{tech}</span>
              </Link>
            ))}
          </div>

          <div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">Examples</div>
          <div className="tw:flex tw:flex-row tw:flex-wrap tw:gap-1 items-center">
            <KeyVal
              k="FreeSewing"
              val="showcase"
              color="secondary"
              href={`/showcase/tags/${design}`}
              Link={Link}
            />
            <KeyVal
              k="Instagram"
              val={`#FreeSewing${capitalize(design)}`}
              color="secondary"
              href={`https://www.instagram.com/explore/search/keyword/?q=%23FreeSewing${capitalize(design)}`}
            />
          </div>

          <div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">Documentation</div>
          <div className="tw:flex tw:flex-row tw:flex-wrap tw:gap-1 items-center">
            <Link href={`/docs/designs/${design}/#notes`}>Designer Notes</Link>,
            <Link href={`/docs/designs/${design}/#needs`}>What You Need</Link>,
            <Link href={`/docs/designs/${design}/#fabric`}>Fabric Options</Link>,
            <Link href={`/docs/designs/${design}/#cutting`}>Cutting Instructions</Link>,
            <Link href={`/docs/designs/${design}/options/`}>Design Options</Link>,
            <Link href={`/docs/designs/${design}/instructions/`}>Sewing Instructions</Link>
          </div>
          <div className="tw:my-4">{buttons}</div>
        </div>
      </div>
    </>
  )
}

const SharingIsCaring = ({ design }) => (
  <>
    <h2>
      Use <b>#FreeSewing{capitalize(design)}</b> to facilitate discovery
    </h2>
    <p>
      Please use the{' '}
      <b>
        <code>#FreeSewing{capitalize(design)}</code>
      </b>{' '}
      hashtag when discussing FreeSewing&apos;s <b>{capitalize(design)}</b> pattern online.
      <br />
      Doing so can help others discover your post, which really is a win-win.
    </p>
    <p>If you like, you can copy the hashtag below:</p>
  </>
)