import type { CarReader } from '@ipld/car'
import type { ISubscription } from '@wovin/core/pubsub'
import type { Accessor, Component } from 'solid-js'
import { useSearchParams } from '@solidjs/router'
import { carFromBlob, tryParseCID } from '@wovin/core'
import { isoDateStrCompare } from '@wovin/core/applog'
import { cidToString } from '@wovin/core/ipfs'
import { toJS } from '@wovin/core/mobx'
import { Logger } from 'besonders-logger'
import { Collapse } from 'solid-collapse'
import { createEffect, createMemo, createResource, createSignal, For, Match, Show, Suspense, Switch } from 'solid-js'
import { boundInput, useAgent } from '../../data/agent/AgentState'
import { DefaultAgentBanner, getSubOrShare } from '../../data/agent/utils-agent'
import { initUseAgent } from '../../data/lazy-agent'
import { decodePubCarWithExtras, retrievePubDataWithExtras } from '../../ipfs/store-sync'
import { doSync } from '../../ipfs/sync-service'
import { PreviewInfoPanel } from '../../ui/preview'
import { useRawThread } from '../../ui/reactive'
import {
	createAsyncButtonHandler,
	makeNote3Url,
	stopPropagation,
	tryParseNote3URL,
	useLocationNavigate,
	useSingleUrlParam,
} from '../../ui/utils-ui'
import { BackLink } from '../BackLink'
import { CidInput } from '../CidInput'
import { CopyableID, DeleteWithConfirm, FileSelect, Iconify, NameValueBadge, PubInfoBox, Spinner } from '../mini-components'

const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line unused-imports/no-unused-vars

export const SubscriptionsSettings: Component<{
	activeSection?: Accessor<string>
	params?: Accessor<string[]>
}> = (
	props,
) => {
	DEBUG('create <SubscriptionSettings>', props)
	// const agent = useAgent()
	initUseAgent()
	const focussedSub = createMemo(() => props.params()?.[0])
	const disabledProp = {} // agent.isAgentStringSetup ? {} : { disabled: 'Disabled until you setup your agent string' } // ? why
	return (
		<div flex='~ col' gap-4>
			<DefaultAgentBanner />
			<Switch>
				<Match when={!focussedSub()}>
					<SubscriptionAdder {...disabledProp} />
				</Match>
				<Match when={focussedSub()}>
					<h4 m-0 flex='inline items-center gap-4'>
						<BackLink text='Back to list' href='/settings/subscriptions' />
					</h4>
				</Match>
			</Switch>

			<SubscriptionsList {...{ focussedSub }} />
		</div>
	)
}

export const SubscriptionsList: Component<{
	focussedSub?: Accessor<string>
}> = (props) => {
	DEBUG('create <SubscriptionsList>')
	const agent = useAgent()
	const { subscriptions } = agent

	const focusedOrList = createMemo(() => {
		let list = props.focussedSub() ? subscriptions.filter(p => p.id === props.focussedSub()) : subscriptions
		list = list.slice().sort((a, b) => isoDateStrCompare(a.createdAt, b.createdAt, 'desc')) // newest first
		DEBUG(`focusedOrList:`, list)
		return list
	})
	createEffect(() => DEBUG(`focussed:`, props.focussedSub()))
	createEffect(() => DEBUG(`subs:`, toJS(subscriptions)))

	return (
		<div flex='~ col items-stretch' gap-2>
			<For
				each={focusedOrList()}
				fallback={(
					<Switch>
						<Match when={props.focussedSub()}>
							<sl-alert variant='danger' open>
								<div flex='~ items-center gap-4' text-xl>
									<div class='i-ph:warning-bold' />
									<strong>Subscription not found:</strong>
								</div>
								<pre mb-0>{props.focussedSub()}</pre>
							</sl-alert>
						</Match>
						<Match when={!props.focussedSub()}>
							No subscriptions (yet)
						</Match>
					</Switch>
				)}
			>
				{(sub: ISubscription, _nodeIdx) => {
					const isFocussed = createMemo(() => props.focussedSub() === sub.id)
					return <SingleSubscriptionPanel sub={sub} {...{ isFocussed }} />
				}}
			</For>
			<style>
				{`.card-overview::part(base){ height: 100%;}`}
			</style>
		</div>
	)
}

export const SubscriptionAdder: Component<{
	// disabled?: string
}> = () => {
	DEBUG('create <SubscriptionAdder>')
	const [urlSearchParams, _setSearchParams] = useSearchParams()
	const [pubToPull] = useSingleUrlParam('pub')
	const appThread = useRawThread()
	// const agent = useAgent()

	const [retrieveID, setRetrieveID] = createSignal(pubToPull() || '')
	// const [retrieving, setRetrieving] = createSignal(false)
	const [subExtras, setSubExtras] = createSignal({})
	const [carFile, setCarFile] = createSignal<CarReader>(null)

	const sanitizedID = createMemo(() => {
		const parsedUrl = tryParseNote3URL(retrieveID())
		if (parsedUrl && parsedUrl.share) {
			DEBUG('parsedUrl:', { retrieveID: retrieveID(), parsedUrl })
			if (parsedUrl.focus) setSubExtras({ selector: `blocks(${parsedUrl.focus})` })
			return parsedUrl.share
			// ? overwrite retrieveID
		}
		return retrieveID()
	})
	const validCID = createMemo(() => {
		const parsed = tryParseCID(sanitizedID())
		DEBUG('validCID?', { sanitizedID: sanitizedID(), parsed })
		if (parsed.cid) {
			return cidToString(parsed.cid)
		}
		return null
	})
	const parseError = createMemo(() => {
		if (!retrieveID() || validCID()) return null
		return tryParseCID(sanitizedID()).errors?.length ? `Invalid URL/CID: '${sanitizedID()}'` : null
	})
	const matchingSubOrShare = createMemo(() => sanitizedID() ? getSubOrShare(sanitizedID()) : null)
	const matchingIsShare = createMemo(() => !!matchingSubOrShare() && (matchingSubOrShare() as any).lastPush !== undefined)

	const [previewData, { refetch: refetchPreviewData }] = createResource(() => ({
		validCID: validCID(),
		matchingSubOrShare: matchingSubOrShare(),
		carFile: carFile(),
	}), async ({ validCID, matchingSubOrShare, carFile }) => {
		if (carFile) {
			const previewData = await decodePubCarWithExtras(appThread, carFile)
			DEBUG.force('previewData from carFile', previewData)
			return previewData
		} else {
			if (matchingSubOrShare) return
			if (!validCID) return null
			return await retrievePubDataWithExtras(appThread, validCID)
		}
	})

	async function loadCar(files: FileList) {
		const file = files[0] // HACK: input allows only single select for now
		LOG(`Loading CAR file`, file)
		const car = await carFromBlob(file)
		setCarFile(car)
	}

	// async function pullAndSave(sub: ISubscription | null, save: boolean = false, retries = 0) {
	// 	// const [encryptedBy, derivationJWKstring] = (encryptAgentSelectRefPull?.value ?? '').split('_|_') ?? []
	// 	setRetrieving(true)
	// 	try {
	// 		if (carFile()) {
	// 			try {
	// 				const { cid, applogs, info } = await decodePubFromCar(carFile())
	// 				await tryIntegrateApplogs(applogs, info.logs, agent.ag, useRawThread(), cid.toString(), cid)
	// 				notifyToast(`Successfully loaded CAR file`, 'success')
	// 				setCarFile(null)
	// 			} catch (err) {
	// 				ERROR(`Failed to load CAR file`, err)

	// 				notifyToast(`Failed to load CAR file`, 'danger')
	// 			}
	// 		} else {
	// 			try {
	// 				const ipns = parseW3Name(sub ? sub.id : (validCID() || examplePullHash))
	// 				DEBUG('retrieving:', { ipns, sub /* encryptedBy, derivationJWKstring,  */ })
	// 				if (!sub) {
	// 					if (matchingIsShare()) throw ERROR(`trying to retrieve from own share`, sub)
	// 					sub = matchingSubOrShare() as (ISubscription | null)
	// 				}
	// 				// const sub = sub as ISubscription|null

	// 				const pullData = await retrievePubToThread(appThread, ipns.toString(), { subID: sub?.id }, agent.ag)
	// 				// TODO: duplicate with the other place
	// 				const updatedFields = {
	// 					lastPullAttempt: dateNowIso(),
	// 					lastCID: pullData.cid.toString(),
	// 					lastApplogCID: pullData.applogsCID.toString(),
	// 				}
	// 				DEBUG('retrieved:', pullData, 'save?', !sub && save, updatedFields)
	// 				if (!sub && save) {
	// 					const sub = await upsertSubscription(ipns.toString(), updatedFields)
	// 					setRetrieveID('')
	// 					navigate('/settings/subscriptions/' + sub.id)
	// 				}
	// 			} catch (err) {
	// 				// throw new Error(`failed to retrieve ${validCID()}`, { cause: err })
	// 				ERROR(`failed to retrieve ${validCID()}`, err)
	// 				if (retries++ < 3) await pullAndSave(sub, save, retries)
	// 				else notifyToast('Failed to pull: ' + (err.message || JSON.stringify(err, undefined, 2)), 'danger')
	// 			}
	// 		}
	// 	} finally {
	// 		setRetrieving(false)
	// 	}
	// }

	return (
		<div flex='~ col'>
			<sl-card class='card-header' w-full style={{ '--border-colly': '#ffffff77', '--padding': 'var(--sl-spacing-medium)' }}>
				<div slot='header'>
					Add subscription
				</div>
				<div flex='~ col' gap-4>
					<Show when={!carFile()}>
						<Show when={pubToPull && sanitizedID() === pubToPull()}>
							<p m-0 text-sm>Share ID from URL:</p>
						</Show>
						<CidInput
							w-full
							placeholder='IPNS (k51q...),  #TODO: IPFS (bafy...), git@..., etc.'
							value={retrieveID}
							onInput={e => setRetrieveID((e.target as HTMLInputElement).value)}
						/>
					</Show>

					<Show when={parseError()}>
						<div text-red>{parseError()}</div>
					</Show>
					<Show when={validCID() || carFile()}>
						<PreviewInfoPanel
							label='Thread'
							targetThread={appThread}
							subID={validCID()}
							previewData={previewData}
							matchingSubOrShare={matchingSubOrShare()}
							refetch={refetchPreviewData}
							ExtraButtons={(
								<a href={validCID() ? makeNote3Url({ pub: validCID(), previewPub: true }) : undefined}>
									{/* target='_blank' */}
									<sl-button size='small' variant='primary' disabled={!validCID() /* && !carFile() */}>
										<Iconify slot='prefix' name='eye-bold' />
										Preview
									</sl-button>
								</a>
							)}
						/>
						{
							/* <div mt--2>
									<PubInfoBox {...{ [isCID() ? 'pinCID' : 'pubID']: validCID(), carFile: carFile() }} />
								</div>
								<Show
									when={matchingSubOrShare()}
								>
									<div>
										This is an&ensp;
										<a href={`#/settings/${matchingIsShare() ? 'shares' : 'subscriptions'}/${matchingSubOrShare().id}`}>
											existing {matchingIsShare() ? 'share' : 'subscription'}
										</a>
									</div>
								</Show>

								<div flex='~ justify-end' gap-2>
									<a href={validCID() ? makeNote3Url({ pub: validCID(), previewPub: true }) : undefined}>
										{/* target='_blank' * /}
										<sl-button size='small' variant='primary' disabled={!validCID() /* && !carFile() * /}>
											<Iconify slot='prefix' name='eye-bold' />
											Preview
										</sl-button>
									</a>
									{/* <sl-button-group label='Pull buttons'> * /}
									<Show when={carFile()}>
										<sl-button
											size='small'
											variant='primary'
											onclick={() => pullAndSave(null, true)}
											loading={retrieving()}
											disabled={(!validCID() && !carFile()) || matchingSubOrShare()}
										>
											<Iconify slot='prefix' name='download-simple-bold' />
											{carFile() ? 'Load' : `Pull & Load`}
										</sl-button>
									</Show>
									<Show when={!carFile()}>
										<SubIntegrateDropdown targetThread={appThread} previewData={previewData()} subID={validCID()} isFullyIntegrated={} />
									</Show>
									{
										/* <sl-dropdown placement='bottom-end'>
										<sl-button slot='trigger' variant='primary' caret>
											<sl-visually-hidden>More pull options</sl-visually-hidden>
										</sl-button>
										<sl-menu>
											<sl-menu-item onclick={() => retrieve(null, false)}>Pull only</sl-menu-item>
											<sl-menu-item disabled>Preview (TODO)</sl-menu-item>
										</sl-menu>
									</sl-dropdown>
								</sl-button-group> * /
									}
								</div>
									*/
						}
					</Show>
					<Show when={!retrieveID() && !carFile()}>
						<FileSelect onFile={files => loadCar(files)}>Load from file</FileSelect>
					</Show>
				</div>
			</sl-card>
		</div>
	)
}

export const SingleSubscriptionPanel: Component<{
	sub: ISubscription
	isFocussed?: Accessor<boolean> | undefined
}> = (props) => {
	const { isFocussed, sub } = props
	const rawDS = useRawThread()
	const agent = useAgent()
	const { locnav, location, navigate } = useLocationNavigate()

	const [isExpanded, setIsExpanded] = createSignal(isFocussed?.())
	const [expandedOnce, setExpandedOnce] = createSignal(isExpanded())
	createEffect(() => {
		if (isFocussed?.()) {
			setIsExpanded(true)
			setExpandedOnce(true)
		} // if it was in the list, but not focussed - it will not be recreated
	})

	async function deleteSub(id) {
		await agent.deleteSub(id)
		if (isFocussed?.()) navigate(`/settings/subscriptions`)
	}
	const pullButtonProps = createAsyncButtonHandler(async () => {
		DEBUG(`Pulling`, props.sub.name, { sub: props.sub, rawDS })
		await doSync({ sub: props.sub.id })
	})

	const onToggleAutopull = e => agent.updateSub(props.sub.id, { autopull: e.target.checked })
	const isAutopull = createMemo(() => {
		return props.sub.autopull
	})

	// TODO: sub data?
	// const subData = /* computed(() =>  */ getPublicationData(rawDS, sub) /* ) */
	// const subRoots = withDS(subData, () => useRoots())
	// const knownAgentPKlogs = rollingFilter(rawDS, { at: 'agent/jwkd' })

	let panelRef, caretRef, buttonRow
	return (
		<div flex='~ col' rounded border='solid 1' border-gray-400>
			<div
				ref={panelRef}
				px-3
				py-2
				flex='~ wrap space-between items-center justify-between'
				gap-2
				bg-white-200
				cursor-pointer
				onClick={(e) => {
					DEBUG(`<SingleSubPanel>.onClick`, e, { panelRef })
					if (e.target !== panelRef && e.target !== caretRef && e.target !== buttonRow) return
					e.stopPropagation()
					setIsExpanded(!isExpanded())
					setExpandedOnce(true)
				}}
			>
				<div mr-2 flex='~ wrap items-center gap-1'>
					<span text-lg font-bold mr-2>
						{boundInput('subscriptionsMap', [], { subID: props.sub.id, padding: 'py-1 px-2' })}
					</span>
					<span text-xs class='opacity-60 hover:opacity-100'>
						{props.sub.lastPull ? <sl-relative-time date={props.sub.lastPull} /> : 'never pulled'}
					</span>
				</div>
				<div ref={buttonRow} flex='~ items-center grow-1 justify-end' gap-2>
					<CopyableID id={props.sub.id} label='Copy IPNS ID' />
					<sl-copy-button
						onClick={stopPropagation}
						m='l--2'
						value={makeNote3Url({ pub: props.sub.id /* block: singleBlockOfSubscription(sub) */ })}
						copy-label='Copy Note3 URL'
					>
						<Iconify name='link' size='5' slot='copy-icon' />
					</sl-copy-button>

					<sl-tooltip onClick={stopPropagation} content='Open preview (new window)'>
						<a
							href={makeNote3Url({ pub: props.sub.id, previewPub: true })}
							target='_blank'
							color='white visited:white'
							flex='inline justify-center'
						>
							<Iconify class='ml--1' size='5' name='eye' />
						</a>
					</sl-tooltip>
					<sl-tooltip placement='top'>
						<Iconify class='mx-1' size='5' name={props.sub.encryptedFor ? 'lock-key-fill' : 'lock-simple-open-light'} />
						<div slot='content'>
							{props.sub.encryptedFor ? `for ${props.sub.encryptedFor}` : 'unencrypted'}
						</div>
					</sl-tooltip>

					<div flex='~ items-center' gap-2>
						<DeleteWithConfirm handler={() => deleteSub(props.sub.id)} />
						<sl-tooltip
							placement='top'
							content={(
								<span>
									<strong>last pull:</strong>
									{' '}
									<Show when={sub.lastPull}>
										<sl-relative-time date={sub.lastPull} />
									</Show>
									<Show when={!sub.lastPull}>
										never
									</Show>
									<br />
									<strong>autopull:</strong>
									{' '}
									{sub.autopull ? 'on' : 'off'}
								</span>
							)}
						>
							<sl-button
								variant={props.sub.autopull ? 'success' : 'default'}
								size='small'
								{...pullButtonProps()}
							>
								<div class='i-ph:download-simple-bold' w-4 h-4 slot='prefix' />
							</sl-button>
						</sl-tooltip>
						<div
							ref={caretRef}
							class={'i-ph:caret-down-bold h-5 w-5 cursor-pointer ' + `${isExpanded() ? 'rotate-180' : 'rotate-0'}`}
							duration-300
							transition-property='[transform]'
						/>
					</div>
				</div>
			</div>
			<Collapse value={isExpanded()} class='duration-300 transition-property-[height]'>
				<Show when={expandedOnce() || isExpanded()} fallback={<div color-green>collapsed content</div>}>
					<Suspense fallback={<Spinner size='3rem' color='purple' />}>
						<div px-3 text-sm flex='~ items-center wrap' gap-1>
							<NameValueBadge name='Created'>
								<sl-relative-time date={props.sub.createdAt} />
							</NameValueBadge>
							<NameValueBadge name='Last pull'>
								<Show when={props.sub.lastPull}>
									<sl-relative-time date={props.sub.lastPull} />
								</Show>
								<Show when={!props.sub.lastPull}>
									never
								</Show>
								<Show when={props.sub.lastCID}>
									⇒
									{' '}
									<CopyableID id={props.sub.lastCID} buttonSize='4' />
								</Show>
							</NameValueBadge>
						</div>
						<div flex='~ wrap justify-items-stretch' px-3 p-2 gap-2>
							<sl-card class='card-overview min-h-32'>
								<div slot='header' flex='~ justify-between'>
									<strong>Pull settings</strong>
								</div>
								<div text-sm>
									Autopull&ensp;
									<sl-switch checked={isAutopull()} onsl-change={onToggleAutopull} size='small' mr--1 />
								</div>
							</sl-card>

							<Show when={isExpanded()}>
								<PubInfoBox label='Data of last pull' pubID={props.sub.id} pinCID={props.sub.lastCID} />
							</Show>
							{
								/* <sl-card class='card-overview min-h-32 md:min-w-20 flex-auto'>
						<div slot='header' flex=''>
							Preview ({subData.size} Applogs,&ensp;<sl-relative-time date={subData.firstLog.ts} />
							➜<sl-relative-time date={subData.latestLog.ts} />)
							<Show when={!!sub.lastCID}>
								<a href={makeNote3Url({ pub: sub.id, previewPub: true })} target='_blank'>
									<sl-button size='small'>
										<div class='i-ph:eye-bold' slot='prefix' />
									</sl-button>
								</a>
							</Show>
						</div>
						<For each={subRoots}>
							{(rootID) => {
								const content = useBlockAt(rootID, 'content')
								return (
									<div>
										- {content.get() as string} (<KidCount block={rootID} /> kids)
									</div>
								)
							}}
						</For>
					</sl-card> */
							}
						</div>
					</Suspense>
				</Show>
			</Collapse>
		</div>
	)
}
