import { useCallback, useEffect, useMemo, useState } from 'react'

import PostOriginalCreatedTimeField from '@/components/PostOriginalCreatedTimeField'
import FieldError from '@/components/common/FieldError'
import FieldWarning from '@/components/common/FieldWarning'
import FormField from '@/components/common/FormField'
import TextArea from '@/components/common/TextArea'
import ThumbnailPreview from '@/components/posts/import/ThumbnailPreview'
import { inputClass } from '@/lib/utils'

import type { FC } from 'react'

import type { PostImportRow } from '@/lib/postImportSession'
import type { DialogueFormControls } from '@/lib/dialogues/useDialogue'

type Draft = {
  url:                   string
  title:                 string
  thumbnailBase:         string
  originalCreatedFrom:   string
  originalCreatedBefore: string
  duration:              string
  tags:                  string
  parentPostIds:         string }

type Props = {
  row:          PostImportRow
  controls:     DialogueFormControls
  onSave:       (args: { draft:          Draft
			 resetRequested: boolean }) => Promise<{
			     saved: boolean
			     row:   PostImportRow | null }> }

const buildDraft = (row: PostImportRow): Draft => ({
    url: row.url,
    title: String (row.attributes.title ?? ''),
    thumbnailBase: String (row.attributes.thumbnailBase ?? ''),
    originalCreatedFrom: String (row.attributes.originalCreatedFrom ?? ''),
    originalCreatedBefore: String (row.attributes.originalCreatedBefore ?? ''),
    duration: String (row.attributes.duration ?? ''),
    tags: String (row.attributes.tags ?? ''),
    parentPostIds: String (row.attributes.parentPostIds ?? '') })

const buildResetDraft = (row: PostImportRow): Draft => ({
    url: row.resetSnapshot.url,
    title: String (row.resetSnapshot.attributes.title ?? ''),
    thumbnailBase: String (row.resetSnapshot.attributes.thumbnailBase ?? ''),
    originalCreatedFrom: String (row.resetSnapshot.attributes.originalCreatedFrom ?? ''),
    originalCreatedBefore: String (row.resetSnapshot.attributes.originalCreatedBefore ?? ''),
    duration: String (row.resetSnapshot.attributes.duration ?? ''),
    tags: String (row.resetSnapshot.attributes.tags ?? ''),
    parentPostIds: String (row.resetSnapshot.attributes.parentPostIds ?? '') })

const groupedMessages = (...values: (string[] | undefined)[]): string[] =>
    values.flatMap (value => value ?? [])

const sameDraft = (left:  Draft, right: Draft): boolean =>
    left.url === right.url
    && left.title === right.title
    && left.thumbnailBase === right.thumbnailBase
    && left.originalCreatedFrom === right.originalCreatedFrom
    && left.originalCreatedBefore === right.originalCreatedBefore
    && left.duration === right.duration
    && left.tags === right.tags
    && left.parentPostIds === right.parentPostIds


const PostImportRowForm: FC<Props> = (
    { row,
      controls,
      onSave },
) => {
  const [draft, setDraft] = useState<Draft> (() => buildDraft (row))
  const [messageRow, setMessageRow] = useState<PostImportRow | null> (null)
  const [saving, setSaving] = useState (false)
  const [resetRequested, setResetRequested] = useState (false)

  useEffect (() => {
    const nextDraft = buildDraft (row)
    setDraft (nextDraft)
    setMessageRow (null)
    setResetRequested (false)
  }, [row])

  const displayRow = messageRow ?? row
  const resetDraft = useMemo (
      () => buildResetDraft (row),
      [row])
  const resetDisabled = saving || sameDraft (draft, resetDraft)

  const update = <Key extends keyof Draft,> (
      key:   Key,
      value: Draft[Key],
  ) => {
    if (messageRow != null)
      setMessageRow (null)
    setDraft (current => ({ ...current, [key]: value }))
  }

  const reset = useCallback (async (): Promise<boolean> => {
    if (resetDisabled)
      return false

    const confirmed = await controls.confirm ({
	title: '変更をリセットしますか?',
	description: '現在の URL に対する自動取得直後の内容へ戻します．',
	confirmText: 'リセット',
	cancelText: '取消',
	variant: 'danger' })
    if (!(confirmed))
      return false

    setDraft (resetDraft)
    setResetRequested (true)
    setMessageRow (null)
    return false
  }, [controls, resetDisabled, resetDraft])

  const save = useCallback (async (): Promise<boolean> => {
    setSaving (true)
    try
      {
	const result = await onSave ({ draft, resetRequested })
	if (result.saved)
	  return true

	if (result.row != null)
	  setMessageRow (result.row)
	return false
      }
    finally
      {
	setSaving (false)
      }
  }, [draft, onSave, resetRequested])

  useEffect (() => {
    controls.setActions ([{
	label: '変更をリセット',
	placement: 'start',
	variant: 'danger',
	disabled: resetDisabled,
	onSelect: reset },
	{
	  label: '編輯内容を保存',
	  disabled: saving,
	  onSelect: save }])
  }, [controls, resetDisabled, reset, save, saving])

  return (
      <>
	<div className="px-6 pb-6">
	  <div className="space-y-4">
	    <div className="grid gap-6 md:grid-cols-[7rem_minmax(0,1fr)]">
	      <div className="space-y-3 md:sticky md:top-0 md:self-start">
		<ThumbnailPreview
		    url={draft.thumbnailBase}
		    className="h-28 w-28"/>
	      </div>

	      <div className="space-y-4">
		<PostImportTextField
		    label="URL"
		    value={draft.url}
		    warnings={displayRow.fieldWarnings.url}
		    errors={groupedMessages (
			displayRow.validationErrors.url,
			displayRow.importErrors?.url)}
		    onChange={value => update ('url', value)}/>
		<PostImportTextField
		    label="タイトル"
		    value={draft.title}
		    warnings={displayRow.fieldWarnings.title}
		    errors={groupedMessages (
			displayRow.validationErrors.title,
			displayRow.importErrors?.title)}
		    onChange={value => update ('title', value)}/>
		<PostImportTextField
		    label="サムネール基底 URL"
		    value={draft.thumbnailBase}
		    warnings={displayRow.fieldWarnings.thumbnailBase}
		    errors={groupedMessages (
			displayRow.validationErrors.thumbnailBase,
			displayRow.importErrors?.thumbnailBase)}
		    onChange={value => update ('thumbnailBase', value)}/>
		<PostOriginalCreatedTimeField
		    originalCreatedFrom={draft.originalCreatedFrom || null}
		    setOriginalCreatedFrom={value => update ('originalCreatedFrom', value ?? '')}
		    originalCreatedBefore={draft.originalCreatedBefore || null}
		    setOriginalCreatedBefore={value => update ('originalCreatedBefore', value ?? '')}
		    errors={groupedMessages (
			displayRow.validationErrors.originalCreatedAt,
			displayRow.validationErrors.originalCreatedFrom,
			displayRow.validationErrors.originalCreatedBefore,
			displayRow.importErrors?.originalCreatedAt,
			displayRow.importErrors?.originalCreatedFrom,
			displayRow.importErrors?.originalCreatedBefore)}/>
		<PostImportTextField
		    label="動画時間"
		    value={draft.duration}
		    errors={groupedMessages (
			displayRow.validationErrors.duration,
			displayRow.validationErrors.videoMs,
			displayRow.importErrors?.duration,
			displayRow.importErrors?.videoMs)}
		    onChange={value => update ('duration', value)}/>
		<PostImportAreaField
		    label="タグ"
		    value={draft.tags}
		    warnings={displayRow.fieldWarnings.tags}
		    errors={groupedMessages (
			displayRow.validationErrors.tags,
			displayRow.importErrors?.tags)}
		    onChange={value => update ('tags', value)}/>
		<PostImportTextField
		    label="親投稿"
		    value={draft.parentPostIds}
		    errors={groupedMessages (
			displayRow.validationErrors.parentPostIds,
			displayRow.importErrors?.parentPostIds)}
		    onChange={value => update ('parentPostIds', value)}/>
		<FieldWarning messages={displayRow.baseWarnings}/>
		<FieldError messages={displayRow.validationErrors.base}/>
		<FieldError messages={displayRow.importErrors?.base}/>
	      </div>
	    </div>
	  </div>
	</div>
      </>)
}

const PostImportTextField = (
    { label, value, warnings, errors, onChange }: {
	label:    string
	value:    string
	warnings?: string[]
	errors?:  string[]
	onChange: (value: string) => void },
) => (
    <FormField label={label} messages={errors}>
      {({ describedBy, invalid }) => (
	  <>
	    <input
		value={value}
		onChange={ev => onChange (ev.target.value)}
		aria-describedby={describedBy}
		aria-invalid={invalid}
		className={inputClass (invalid)}/>
	    <FieldWarning messages={warnings}/>
	  </>)}
    </FormField>)

const PostImportAreaField = (
    { label, value, warnings, errors, onChange }: {
	label:    string
	value:    string
	warnings?: string[]
	errors?:  string[]
	onChange: (value: string) => void },
) => (
    <FormField label={label} messages={errors}>
      {({ describedBy, invalid }) => (
	  <>
	    <TextArea
		value={value}
		rows={4}
		onChange={ev => onChange (ev.target.value)}
		aria-describedby={describedBy}
		invalid={invalid}
		className="h-auto"/>
	    <FieldWarning messages={warnings}/>
	  </>)}
    </FormField>)

export default PostImportRowForm
export { buildDraft }
export type { Draft as PostImportRowDraft }
