// Dependencies
import {
  measurements,
  isDegreeMeasurement,
  control as controlConfig,
  urls,
} from '@freesewing/config'
import { measurements as measurementTranslations } from '@freesewing/i18n'
import { measurements as designMeasurements } from '@freesewing/collection'
import {
  cloudflareImageUrl,
  capitalize,
  formatMm,
  horFlexClasses,
  linkClasses,
  notEmpty,
  roundDistance,
  shortDate,
  timeAgo,
} from '@freesewing/utils'
// Context
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
import { ModalContext } from '@freesewing/react/context/Modal'
// Hooks
import React, { useState, useEffect, Fragment, useContext } from 'react'
import { useAccount } from '@freesewing/react/hooks/useAccount'
import { useBackend } from '@freesewing/react/hooks/useBackend'
// Components
import { Link as WebLink, AnchorLink } from '@freesewing/react/components/Link'
import {
  BoolNoIcon,
  BoolYesIcon,
  CloneIcon,
  CuratedMeasurementsSetIcon,
  EditIcon,
  ShowcaseIcon,
  NewMeasurementsSetIcon,
  NoIcon,
  OkIcon,
  PlusIcon,
  ResetIcon,
  TrashIcon,
  UploadIcon,
  //  WarningIcon,
  //  BoolYesIcon,
  //  BoolNoIcon,
} from '@freesewing/react/components/Icon'
import { BookmarkButton, MsetCard } from '@freesewing/react/components/Account'
import {
  DesignInput,
  MarkdownInput,
  ListInput,
  MeasurementInput,
  PassiveImageInput,
  StringInput,
  ToggleInput,
} from '@freesewing/react/components/Input'
import { DisplayRow } from './shared.mjs'
import Markdown from 'react-markdown'
import { ModalWrapper } from '@freesewing/react/components/Modal'
import { Json } from '@freesewing/react/components/Json'
import { Yaml } from '@freesewing/react/components/Yaml'
import { Popout } from '@freesewing/react/components/Popout'

const t = (input) => {
  console.log('t called', input)
  return input
}

/*
 * Component to show an individual measurements set
 *
 * @param {object} props - All React props
 * @param {number} id - The ID of the measurements set to load
 * @param {bool} publicOnly - FIXME
 * @param {function} Link - An optional framework-specific Link component to use for client-side routing
 */
export const Set = ({ id, publicOnly = false, Link = false }) => {
  if (!Link) Link = WebLink

  // Hooks
  const { account, control } = useAccount()
  const { setLoadingStatus } = useContext(LoadingStatusContext)
  const backend = useBackend()

  // Context
  const { setModal } = useContext(ModalContext)

  const [filter, setFilter] = useState(false)
  const [edit, setEdit] = useState(false)
  const [suggest, setSuggest] = useState(false)
  const [mset, setMset] = useState()
  // Set fields for editing
  const [name, setName] = useState(mset?.name)
  const [image, setImage] = useState(mset?.image)
  const [isPublic, setIsPublic] = useState(mset?.public ? true : false)
  const [imperial, setImperial] = useState(mset?.imperial ? true : false)
  const [notes, setNotes] = useState(mset?.notes || '')
  const [measies, setMeasies] = useState({})
  const [displayAsMetric, setDisplayAsMetric] = useState(mset?.imperial ? false : true)

  // Effect
  useEffect(() => {
    const getSet = async () => {
      setLoadingStatus([true, 'Contacting the backend'])
      const [status, body] = await backend.getSet(id)
      if (status === 200 && body.result === 'success') {
        setMset(body.set)
        setName(body.set.name)
        setImage(body.set.image)
        setIsPublic(body.set.public ? true : false)
        setImperial(body.set.imperial ? true : false)
        setNotes(body.set.notes)
        setMeasies(body.set.measies)
        setLoadingStatus([true, 'Measurements set loaded', true, true])
      } else setLoadingStatus([true, 'An error occured while contacting the backend', true, false])
    }
    const getPublicSet = async () => {
      setLoadingStatus([true, 'Contacting the backend'])
      const [status, body] = await backend.getPublicSet(id)
      if (status === 200 && body.result === 'success') {
        setMset({
          ...body.data,
          public: true,
          measies: body.data.measurements,
        })
        setName(body.data.name)
        setImage(body.data.image)
        setIsPublic(body.data.public ? true : false)
        setImperial(body.data.imperial ? true : false)
        setNotes(body.data.notes)
        setMeasies(body.data.measurements)
        setLoadingStatus([true, 'Measurements set loaded', true, true])
      } else
        setLoadingStatus([
          true,
          'An error occured while loading this measurements set',
          true,
          false,
        ])
    }
    if (id) {
      if (publicOnly) getPublicSet()
      else getSet()
    }
  }, [id, publicOnly])

  const filterMeasurements = () =>
    filter ? designMeasurements[filter].sort() : measurements.sort()

  if (!id || !mset) return null

  const updateMeasies = (m, val) => {
    const newMeasies = { ...measies }
    newMeasies[m] = val
    setMeasies(newMeasies)
  }

  const save = async () => {
    setLoadingStatus([true, 'Gathering info'])
    // Compile data
    const data = { measies: {} }
    if (name || name !== mset.name) data.name = name
    if (image || image !== mset.image) data.img = image
    if ([true, false].includes(isPublic) && isPublic !== mset.public) data.public = isPublic
    if ([true, false].includes(imperial) && imperial !== mset.imperial) data.imperial = imperial
    if (notes || notes !== mset.notes) data.notes = notes
    // Add measurements
    for (const m of measurements) {
      if (measies[m] || measies[m] !== mset.measies[m]) data.measies[m] = measies[m]
    }
    setLoadingStatus([true, 'Saving measurements set'])
    const [status, body] = await backend.updateSet(mset.id, data)
    if (status === 200 && body.result === 'success') {
      setMset(body.set)
      setEdit(false)
      setLoadingStatus([true, 'Nailed it', true, true])
    } else
      setLoadingStatus([true, 'That did not go as planned. Saving the set failed.', true, false])
  }

  const togglePublic = async () => {
    setLoadingStatus([true, 'Getting ready'])
    const [status, body] = await backend.updateSet(mset.id, { public: !mset.public })
    if (status === 200 && body.result === 'success') {
      setMset(body.set)
      setLoadingStatus([true, 'Alright, done!', true, true])
    } else setLoadingStatus([true, 'Backend says no :(', true, false])
  }

  const importSet = async () => {
    setLoadingStatus([true, 'Importing data'])
    // Compile data
    const data = {
      ...mset,
      userId: account.id,
      measies: { ...mset.measies },
    }
    delete data.img
    const [status, body] = await backend.createSet(data)
    if (status === 201 && body.result === 'created') {
      setLoadingStatus([true, 'Loading newly created set', true, true])
      window.location = `/account/data/sets/set?id=${body.set.id}`
    } else setLoadingStatus([true, 'We failed to create this measurements set', true, false])
  }

  const heading = (
    <>
      <div className="tw-flex tw-flex-wrap md:tw-flex-nowrap tw-flex-row tw-gap-2 tw-w-full">
        <div className="tw-w-full md:tw-w-96 tw-shrink-0">
          <MsetCard set={mset} control={control} Link={Link} />
        </div>
        <div className="tw-flex tw-flex-col tw-justify-end tw-gap-2 tw-mb-2 tw-grow">
          {account.control > 2 && mset.public && mset.userId !== account.id ? (
            <div className="tw-flex tw-flex-row tw-gap-2 tw-items-center">
              <a
                className="tw-daisy-badge tw-daisy-badge-secondary tw-font-bold tw-daisy-badge-lg"
                href={`${urls.backend}/sets/${mset.id}.json`}
              >
                JSON
              </a>
              <a
                className="tw-daisy-badge tw-daisy-badge-success tw-font-bold tw-daisy-badge-lg"
                href={`${urls.backend}/sets/${mset.id}.yaml`}
              >
                YAML
              </a>
            </div>
          ) : (
            <span></span>
          )}
          {account.control > 3 && mset.userId === account.id ? (
            <div className="tw-flex tw-flex-row tw-gap-2 tw-items-center">
              <button
                className="tw-daisy-badge tw-daisy-badge-secondary tw-font-bold tw-daisy-badge-lg"
                onClick={() =>
                  setModal(
                    <ModalWrapper keepOpenOnClick>
                      <Json js={mset} />
                    </ModalWrapper>
                  )
                }
              >
                JSON
              </button>
              <button
                className="tw-daisy-badge tw-daisy-badge-success tw-font-bold tw-daisy-badge-lg tw-text-neutral-content"
                onClick={() =>
                  setModal(
                    <ModalWrapper keepOpenOnClick>
                      <Yaml js={mset} />
                    </ModalWrapper>
                  )
                }
              >
                YAML
              </button>
            </div>
          ) : (
            <span></span>
          )}
          {account.id && account.control > 2 && mset.public && mset.userId !== account.id ? (
            <button
              className="tw-daisy-btn tw-daisy-btn-primary"
              title="Import measurements set"
              onClick={importSet}
            >
              <div className="tw-flex tw-flex-row tw-gap-4 tw-justify-between tw-items-center tw-w-full">
                <UploadIcon />
                Import measurements set
              </div>
            </button>
          ) : null}
          {account.control > 2 ? (
            <BookmarkButton slug={`sets/${mset.id}`} title={mset.name} type="set" thing="set" />
          ) : null}
          <button
            onClick={() =>
              setModal(
                <ModalWrapper flex="col" justify="top lg:justify-center" slideFrom="right">
                  <img src={cloudflareImageUrl({ type: 'public', id: mset.img })} />
                </ModalWrapper>
              )
            }
            className={`tw-daisy-btn tw-daisy-btn-secondary tw-btn-outline ${horFlexClasses}`}
          >
            <ShowcaseIcon />
            Show Image
          </button>
          {!publicOnly && (
            <>
              {account.control > 2 ? (
                <button
                  onClick={() => {
                    setSuggest(!suggest)
                    setEdit(false)
                  }}
                  className={`tw-daisy-btn ${
                    suggest ? 'tw-daisy-btn-neutral' : 'tw-daisy-btn-primary'
                  } tw-daisy-btn-outline ${horFlexClasses}`}
                >
                  {suggest ? <ResetIcon /> : <CuratedMeasurementsSetIcon />}
                  {suggest ? 'Cancel' : 'Suggest for curation'}
                </button>
              ) : null}
              {edit ? (
                <>
                  <button
                    onClick={() => {
                      setEdit(false)
                      setSuggest(false)
                    }}
                    className={`tw-daisy-btn tw-daisy-btn-neutral tw-daisy-btn-outline ${horFlexClasses}`}
                  >
                    <ResetIcon />
                    Cancel
                  </button>
                  <button
                    onClick={save}
                    className={`tw-daisy-btn tw-daisy-btn-primary ${horFlexClasses}`}
                  >
                    <UploadIcon />
                    Save measurements set
                  </button>
                </>
              ) : (
                <button
                  onClick={() => {
                    setEdit(true)
                    setSuggest(false)
                  }}
                  className={`tw-daisy-btn tw-daisy-btn-primary ${horFlexClasses}`}
                >
                  <EditIcon /> Edit measurements set
                </button>
              )}
            </>
          )}
          {account.control > 2 && mset.userId === account.id ? (
            <button
              className="tw-daisy-btn tw-daisy-btn-neutral"
              title="Clone measurements set"
              onClick={importSet}
            >
              <div className="tw-flex tw-flex-row tw-gap-4 tw-justify-between tw-items-center tw-w-full">
                <CloneIcon />
                Clone measurements set
              </div>
            </button>
          ) : null}
        </div>
      </div>
      <div className="tw-flex tw-flex-row tw-flex-wrap tw-gap-4 tw-text-sm tw-items-center tw-justify-between tw-mb-2"></div>
    </>
  )

  if (suggest)
    return (
      <div className="tw-w-full">
        {heading}
        <SuggestCset {...{ mset, setLoadingStatus, backend, Link }} />
      </div>
    )

  if (!edit)
    return (
      <div className="tw-w-full">
        {heading}

        <h2>Data</h2>
        <DisplayRow title="Name">{mset.name}</DisplayRow>
        <DisplayRow title="Units">{mset.imperial ? 'Imperial' : 'Metric'}</DisplayRow>
        {control >= controlConfig.account.sets.notes && (
          <DisplayRow title="Notes">
            <Markdown>{mset.notes}</Markdown>
          </DisplayRow>
        )}
        {control >= controlConfig.account.sets.public && (
          <>
            {mset.userId === account.id && (
              <DisplayRow title="Public">
                <div className="tw-flex tw-flex-row tw-gap-2 tw-items-center tw-justify-between">
                  {mset.public ? (
                    <OkIcon className="tw-w-6 tw-h-6 tw-text-success" stroke={4} />
                  ) : (
                    <NoIcon className="tw-w-6 tw-h-6 tw-text-error" stroke={3} />
                  )}
                  <button
                    className="tw-daisy-btn tw-daisy-btn-secondary tw-daisy-btn-sm"
                    onClick={togglePublic}
                  >
                    Make {mset.public ? 'Private' : 'Public'}
                  </button>
                </div>
              </DisplayRow>
            )}
            {mset.public && (
              <DisplayRow title="Permalink">
                <Link
                  href={`/set?id=${mset.id}`}
                  className={linkClasses}
                >{`/set?id=${mset.id}`}</Link>
              </DisplayRow>
            )}
          </>
        )}
        {control >= controlConfig.account.sets.createdAt && (
          <DisplayRow title="Created">
            {timeAgo(mset.createdAt, false)}
            <span className="tw-text-sm tw-pl-2">({shortDate(mset.createdAt, false)})</span>
          </DisplayRow>
        )}
        {control >= controlConfig.account.sets.updatedAt && (
          <DisplayRow title="Updated">
            {timeAgo(mset.updatedAt, false)}
            <span className="tw-text-sm tw-pl-2">({shortDate(mset.updatedAt, false)})</span>
          </DisplayRow>
        )}
        {control >= controlConfig.account.sets.id && <DisplayRow title="ID">{mset.id}</DisplayRow>}

        {Object.keys(mset.measies).length > 0 && (
          <>
            <h2>Measurements</h2>
            <ToggleInput
              label={false}
              labels={['Metric Units (cm)', 'Imperial Units (inch)']}
              update={() => setDisplayAsMetric(!displayAsMetric)}
              current={displayAsMetric}
            />
            {Object.entries(mset.measies).map(([m, val]) =>
              val > 0 ? (
                <DisplayRow
                  title={<MeasurementValue {...{ m, val, imperial: !displayAsMetric }} />}
                  key={m}
                >
                  <span className="tw-font-medium">{m}</span>
                </DisplayRow>
              ) : null
            )}
          </>
        )}
      </div>
    )

  return (
    <div className="tw-w-full">
      {heading}
      <h2 id="measies">Measurements</h2>
      <div className="tw-bg-secondary tw-px-4 tw-pt-1 tw-pb-4 tw-rounded-lg tw-shadow tw-bg-opacity-10">
        <DesignInput
          update={setFilter}
          label="Filter by design"
          current={filter}
          firstOption={<option value="">Clear filter</option>}
        />
      </div>
      {filterMeasurements().map((m) => (
        <MeasurementInput
          id={`measie-${m}`}
          key={m}
          m={m}
          imperial={mset.imperial}
          label={measurementTranslations[m]}
          current={mset.measies[m]}
          original={mset.measies[m]}
          update={updateMeasies}
        />
      ))}

      <h2 id="data">Data</h2>

      {/* Name is always shown */}
      <span id="name"></span>
      <StringInput
        id="set-name"
        label="Name"
        update={setName}
        current={name}
        original={mset.name}
        placeholder="Georg Cantor"
        valid={(val) => val && val.length > 0}
      />

      {/* img: Control level determines whether or not to show this */}
      <span id="image"></span>
      {account.control >= controlConfig.account.sets.img ? (
        <PassiveImageInput
          id="set-img"
          label="Image"
          update={setImage}
          current={image}
          valid={(val) => val.length > 0}
        />
      ) : null}

      {/* public: Control level determines whether or not to show this */}
      <span id="public"></span>
      {account.control >= controlConfig.account.sets.public ? (
        <ListInput
          id="set-public"
          label="Public"
          update={setIsPublic}
          list={[
            {
              val: true,
              label: (
                <div className="tw-flex tw-flex-row tw-items-center tw-flex-wrap tw-justify-between tw-w-full">
                  <span>Public measurements set</span>
                  <OkIcon
                    className="tw-w-8 tw-h-8 tw-text-success tw-bg-base-100 tw-rounded-full tw-p-1"
                    stroke={4}
                  />
                </div>
              ),
              desc: 'Others are allowed to use these measurements to generate or test patterns',
            },
            {
              val: false,
              label: (
                <div className="tw-flex tw-flex-row tw-items-center tw-flex-wrap tw-justify-between tw-w-full">
                  <span>Private measurements set</span>
                  <NoIcon
                    className="tw-w-8 tw-h-8 tw-text-error tw-bg-base-100 tw-rounded-full tw-p-1"
                    stroke={3}
                  />
                </div>
              ),
              desc: 'These measurements cannot be used by other users or visitors',
            },
          ]}
          current={isPublic}
        />
      ) : null}

      {/* units: Control level determines whether or not to show this */}
      <span id="units"></span>
      {account.control >= controlConfig.account.sets.units ? (
        <>
          <ListInput
            id="set-units"
            label="Units"
            update={setImperial}
            list={[
              {
                val: false,
                label: (
                  <div className="tw-flex tw-flex-row tw-items-center tw-flex-wrap tw-justify-between tw-w-full">
                    <span>Metric units (cm)</span>
                    <span className="tw-text-inherit tw-text-2xl tw-pr-2">cm</span>
                  </div>
                ),
                desc: 'Pick this if you prefer cm over inches',
              },
              {
                val: true,
                label: (
                  <div className="tw-flex tw-flex-row tw-items-center tw-flex-wrap tw-justify-between tw-w-full">
                    <span>Imperial units (inch)</span>
                    <span className="tw-text-inherit tw-text-4xl tw-pr-2">″</span>
                  </div>
                ),
                desc: 'Pick this if you prefer inches over cm',
              },
            ]}
            current={imperial}
          />
          <span className="tw-text-large tw-text-warning">
            Note: You must save after changing Units to have the change take effect on this page.
          </span>
        </>
      ) : null}

      {/* notes: Control level determines whether or not to show this */}
      <span id="notes"></span>
      {account.control >= controlConfig.account.sets.notes ? (
        <MarkdownInput
          id="set-notes"
          label="Notes"
          update={setNotes}
          current={notes}
          placeholder="You can use markdown here"
        />
      ) : null}
      <button
        onClick={save}
        className="tw-daisy-btn tw-daisy-btn-primary tw-daisy-btn-lg tw-flex tw-flex-row tw-items-center tw-gap-4 tw-mx-auto tw-mt-8"
      >
        <UploadIcon />
        Save Measurements Set
      </button>
    </div>
  )
}

/**
 * A (helper) component to display a measurements value
 *
 * @param {object} props - All React props
 * @param {string} val - The value
 * @param {string} m - The measurement name
 * @param {bool} imperial - True for imperial measurements, or metric by default
 */
export const MeasurementValue = ({ val, m, imperial = false }) =>
  isDegreeMeasurement(m) ? (
    <span>{val}°</span>
  ) : (
    <span dangerouslySetInnerHTML={{ __html: formatMm(val, imperial) }}></span>
  )

/**
 * React component to suggest a measurements set for curation
 *
 * @param {object} props - All React props
 * @param {string} mset - The measurements set
 */
export const SuggestCset = ({ mset, Link }) => {
  // State
  const [height, setHeight] = useState('')
  const [img, setImg] = useState('')
  const [name, setName] = useState('')
  const [notes, setNotes] = useState('')
  const [submission, setSubmission] = useState(false)

  console.log(mset)

  // Hooks
  const backend = useBackend()

  // Method to submit the form
  const suggestSet = async () => {
    setLoadingStatus([true, 'Contacting backend'])
    const result = await backend.suggestCset({ set: mset.id, height, img, name, notes })
    if (result.success && result.data.submission) {
      setSubmission(result.data.submission)
      setLoadingStatus([true, 'Nailed it', true, true])
    } else setLoadingStatus([true, 'An unexpected error occured. Please report this.', true, false])
  }

  const missing = []
  for (const m of measurements) {
    if (typeof mset.measies[m] === 'undefined') missing.push(m)
  }

  if (submission) {
    const url = `/curate/sets/suggested/${submission.id}`

    return (
      <>
        <h2>Thank you</h2>
        <p>Your submission has been registered and will be processed by one of our curators.</p>
        <p>
          It is available at: <Link href={url}>{url}</Link>
        </p>
      </>
    )
  }

  return (
    <>
      <h2>Suggest a measurements set for curation</h2>
      <h4 className="tw-flex tw-flex-row tw-items-center tw-gap-2">
        {missing.length > 0 ? <BoolNoIcon /> : <BoolYesIcon />}
        Measurements
      </h4>
      {missing.length > 0 ? (
        <>
          <p>
            To ensure curated measurements sets work for all designs, you need to provide a full set
            of measurements.
          </p>
          <p>Your measurements set is missing the following measurements:</p>
          <ul className="tw-list tw-list-inside tw-list-disc tw-ml-4">
            {missing.map((m) => (
              <li key={m}>{m}</li>
            ))}
          </ul>
        </>
      ) : (
        <p>All measurements are available.</p>
      )}
      <h4 className="tw-flex tw-flex-row tw-items-center tw-gap-2">
        {name.length > 1 ? <BoolYesIcon /> : <BoolNoIcon />}
        Name
      </h4>
      <p>Each curated set has a name. You can suggest your own name or a pseudonym.</p>
      <StringInput label="Name" current={name} update={setName} valid={(val) => val.length > 1} />
      <h4 className="tw-flex tw-flex-row tw-items-center tw-gap-2">
        {height.length > 1 ? <BoolYesIcon /> : <BoolNoIcon />}
        Height
      </h4>
      <p>
        To allow organizing and presenting our curated sets in a structured way, we organize them by
        height.
      </p>
      <StringInput
        label="height"
        current={height}
        update={setHeight}
        valid={(val) => val.length > 1}
      />
      <h4 className="tw-flex tw-flex-row tw-items-center tw-gap-2 tw-mt-4">
        {img.length > 0 ? <BoolYesIcon /> : <BoolNoIcon />}
        Image
      </h4>
      <p>
        Finally, we need a picture. Please refer to the documentation to see what makes a good
        picture for a curated measurements set.
        <Link href="/docs/about/site/csets">Documentation</Link>
      </p>
      <PassiveImageInput
        label="Image"
        current={img}
        update={setImg}
        valid={(val) => val.length > 1}
      />
      <h4 className="tw-flex tw-flex-row tw-items-center tw-gap-2 tw-mt-4">
        <BoolYesIcon />
        Notes
      </h4>
      <p>If you would like to add any notes, you can do so here.</p>
      <Popout tip compact>
        This field supports markdown
      </Popout>
      <MarkdownInput label="Notes" current={notes} update={setNotes} valid={() => true} />
      <button
        className="tw-daisy-btn tw-daisy-btn-primary tw-w-full tw-mt-4"
        disabled={!(missing.length === 0 && height.length > 1 && img.length > 0)}
        onClick={suggestSet}
      >
        Suggest for curation
      </button>
    </>
  )
}

export const NewSet = () => {
  // Hooks
  const backend = useBackend()
  const { account } = useAccount()
  const { setLoadingStatus, LoadingProgress } = useContext(LoadingStatusContext)

  // State
  const [name, setName] = useState('')

  // Use account setting for imperial
  const imperial = account.imperial

  // Helper method to create a new set
  const createSet = async () => {
    setLoadingStatus([true, 'Storing new measurements set'])
    const [status, body] = await backend.createSet({ name, imperial })
    if (status === 201 && body.result === 'created') {
      setLoadingStatus([true, 'Nailed it', true, true])
      window.location = `/account/data/sets/set?id=${body.set.id}`
    } else
      setLoadingStatus([
        true,
        'Failed to save the measurments set. Please report this.',
        true,
        false,
      ])
  }

  return (
    <div className="tw-max-w-xl">
      <h5>Name</h5>
      <p>Give this set of measurements a name. That will help tell them apart.</p>
      <StringInput
        id="new-set"
        label="Name"
        update={setName}
        current={name}
        valid={(val) => val && val.length > 0}
        placeholder={'Georg Cantor'}
      />
      <div className="tw-flex tw-flex-row tw-gap-2 tw-items-center tw-w-full tw-mt-8 tw-mb-2">
        <button
          className="tw-daisy-btn tw-daisy-btn-primary tw-grow tw-capitalize"
          disabled={name.length < 1}
          onClick={createSet}
        >
          New Measurements Set
        </button>
      </div>
    </div>
  )
}