'use client';

import {
  ComponentProps,
  ReactNode,
} from 'react';
import { clsx } from 'clsx/lite';
import ChecklistRow from '../components/ChecklistRow';
import { FiExternalLink } from 'react-icons/fi';
import {
  BiCog,
  BiCopy,
  BiData,
  BiLockAlt,
  BiPencil,
} from 'react-icons/bi';
import Container from '@/components/Container';
import Checklist from '@/components/Checklist';
import { toastSuccess } from '@/toast';
import { ConfigChecklistStatus } from './config';
import StatusIcon from '@/components/StatusIcon';
import { labelForStorage } from '@/services/storage';
import { HiSparkles } from 'react-icons/hi';
import LoaderButton from '@/components/primitives/LoaderButton';
import { testConnectionsAction } from '@/admin/actions';
import ErrorNote from '@/components/ErrorNote';
import Spinner from '@/components/Spinner';
import WarningNote from '@/components/WarningNote';

export default function SiteChecklistClient({
  // Config checklist
  hasDatabase,
  isPostgresSslEnabled,
  hasVercelPostgres,
  hasVercelKv,
  hasStorageProvider,
  hasVercelBlobStorage,
  hasCloudflareR2Storage,
  hasAwsS3Storage,
  hasMultipleStorageProviders,
  currentStorage,
  hasAuthSecret,
  hasAdminUser,
  hasDomain,
  hasTitle,
  hasDescription,
  hasAbout,
  hasDefaultTheme,
  showRepoLink,
  showSocial,
  showFilmSimulations,
  showExifInfo,
  defaultTheme,
  areOriginalUploadsPreserved,
  isGridHomepageEnabled,
  isStaticallyOptimized,
  arePhotosStaticallyOptimized,
  arePhotoOGImagesStaticallyOptimized,
  arePhotoCategoriesStaticallyOptimized,
  arePhotosMatted,
  isBlurEnabled,
  isGeoPrivacyEnabled,
  isPriorityOrderEnabled,
  isAiTextGenerationEnabled,
  aiTextAutoGeneratedFields,
  hasAiTextAutoGeneratedFields,
  isPublicApiEnabled,
  arePublicDownloadsEnabled,
  isOgTextBottomAligned,
  gridAspectRatio,
  hasGridAspectRatio,
  gridDensity,
  hasGridDensityPreference,
  // Connection status
  databaseError,
  storageError,
  kvError,
  aiError,
  // Component props
  simplifiedView,
  isTestingConnections,
  secret,
  baseUrl,
  commitSha,
  commitMessage,
}: ConfigChecklistStatus &
  Partial<Awaited<ReturnType<typeof testConnectionsAction>>> & {
  simplifiedView?: boolean
  isTestingConnections?: boolean
  secret?: string
}) {
  const renderLink = (href: string, text: string, external = true) =>
    <>
      <a {...{
        href,
        ...external && { target: '_blank', rel: 'noopener noreferrer' },
        className: clsx(
          'underline hover:no-underline',
        ),
      }}>
        {text}
      </a>
      {external &&
        <>
          &nbsp;
          <FiExternalLink
            size={14}
            className='inline translate-y-[-1.5px]'
          />
        </>}
    </>;

  const renderCopyButton = (label: string, text?: string, subtle?: boolean) =>
    <LoaderButton
      icon={<BiCopy size={15} />}
      className={clsx(
        'translate-y-[2px]',
        subtle && 'text-gray-300 dark:text-gray-700',
      )}
      onClick={text
        ? () => {
          navigator.clipboard.writeText(text);
          toastSuccess(`${label} copied to clipboard`);
        }
        : undefined}
      styleAs="link"
      disabled={!text}
    />;

  const renderEnvVar = (
    variable: string,
    minimal?: boolean,
  ) =>
    <div
      key={variable}
      className={clsx(
        'overflow-x-auto overflow-y-hidden',
        minimal && 'inline-flex',
      )}
    >
      <span className="inline-flex items-center gap-1">
        <span className={clsx(
          'text-[11px] font-medium tracking-wider',
          'px-0.5 py-[0.5px]',
          'rounded-[5px]',
          'bg-gray-100 dark:bg-gray-800',
        )}>
          `{variable}`
        </span>
        {!minimal && renderCopyButton(variable, variable, true)}
      </span>
    </div>;

  const renderEnvVars = (variables: string[]) =>
    <div className="pt-1 space-y-1">
      {variables.map(envVar => renderEnvVar(envVar))}
    </div>;

  const renderSubStatus = (
    type: ComponentProps<typeof StatusIcon>['type'],
    label: ReactNode,
    iconClassName?: string,
  ) =>
    <div className="flex gap-2 translate-x-[-3px]">
      <span className={iconClassName}>
        <StatusIcon {...{ type }} />
      </span>
      <span className="min-w-0">
        {label}
      </span>
    </div>;
    
  const renderSubStatusWithEnvVar = (
    type: ComponentProps<typeof StatusIcon>['type'],
    variable: string,
  ) =>
    renderSubStatus(
      type,
      renderEnvVars([variable]),
      'translate-y-[5px]',
    );

  const renderError = ({
    connection,
    message,
  }: {
    connection?: { provider: string, error: string }
    message?: string
  }) =>
    <ErrorNote className="mt-2 mb-3">
      {connection && <>
        {connection.provider} connection error: {`"${connection.error}"`}
      </>}
      {message}
    </ErrorNote>;

  const renderWarning = ({
    connection,
    message,
  }: {
    connection?: { provider: string, error: string }
    message?: string
  }) =>
    <WarningNote className="mt-2 mb-3">
      {connection && <>
        {connection.provider} connection error: {`"${connection.error}"`}
      </>}
      {message}
    </WarningNote>;

  return (
    <div className="max-w-xl w-full">
      <div className="space-y-6">
        <Checklist
          title="Storage"
          icon={<BiData size={16} />}
        >
          <ChecklistRow
            title={hasDatabase && isTestingConnections
              ? 'Testing database connection'
              : 'Setup database'}
            status={hasDatabase}
            isPending={hasDatabase && isTestingConnections}
          >
            {databaseError && renderError({
              connection: { provider: 'Database', error: databaseError},
            })}
            {hasVercelPostgres
              ? renderSubStatus('checked', 'Vercel Postgres: connected')
              : renderSubStatus('optional', <>
                Vercel Postgres:
                {' '}
                {renderLink(
                  // eslint-disable-next-line max-len
                  'https://vercel.com/docs/storage/vercel-postgres/quickstart#create-a-postgres-database',
                  'create store',
                )}
                {' '}
                and connect to project
              </>)}
            {hasDatabase && !hasVercelPostgres &&
              renderSubStatus('checked', <>
                Postgres-compatible: connected
                {' '}
                (SSL {isPostgresSslEnabled ? 'enabled' : 'disabled'})
              </>)}
          </ChecklistRow>
          <ChecklistRow
            title={
              hasStorageProvider && isTestingConnections
                ? 'Testing storage connection'
                : !hasStorageProvider
                  ? 'Setup storage (one of the following)'
                  : hasMultipleStorageProviders
                    // eslint-disable-next-line max-len
                    ? `Setup storage (new uploads go to: ${labelForStorage(currentStorage)})`
                    : 'Setup storage'}
            status={hasStorageProvider}
            isPending={hasStorageProvider && isTestingConnections}
          >
            {storageError && renderError({
              connection: { provider: 'Storage', error: storageError},
            })}
            {hasVercelBlobStorage
              ? renderSubStatus('checked', 'Vercel Blob: connected')
              : renderSubStatus('optional', <>
                {labelForStorage('vercel-blob')}:
                {' '}
                {renderLink(
                  // eslint-disable-next-line max-len
                  'https://vercel.com/docs/storage/vercel-blob/quickstart#create-a-blob-store',
                  'create store',
                )}
                {' '} 
                and connect to project
              </>,
              )}
            {hasCloudflareR2Storage
              ? renderSubStatus('checked', 'Cloudflare R2: connected')
              : renderSubStatus('optional', <>
                {labelForStorage('cloudflare-r2')}:
                {' '}
                {renderLink(
                  'https://github.com/sambecker/exif-photo-blog#cloudflare-r2',
                  'create/configure bucket',
                )}
              </>)}
            {hasAwsS3Storage
              ? renderSubStatus('checked', 'AWS S3: connected')
              : renderSubStatus('optional', <>
                {labelForStorage('aws-s3')}:
                {' '}
                {renderLink(
                  'https://github.com/sambecker/exif-photo-blog#aws-s3',
                  'create/configure bucket',
                )}
              </>)}
          </ChecklistRow>
        </Checklist>
        <Checklist
          title="Authentication"
          icon={<BiLockAlt size={16} />}
        >
          <ChecklistRow
            title={!hasAuthSecret && isTestingConnections
              ? 'Generating secret'
              : 'Setup auth'}
            status={hasAuthSecret}
            isPending={!hasAuthSecret && isTestingConnections}
          >
            Store auth secret in environment variable:
            {!hasAuthSecret &&
              <div className="overflow-x-auto">
                <Container className="my-1.5 inline-flex" padding="tight">
                  <div className={clsx(
                    'flex flex-nowrap items-center gap-2 leading-none -mx-1',
                  )}>
                    {secret ? <span>{secret}</span> : <Spinner />}
                    <div
                      className="flex items-center gap-0.5 translate-y-[-2px]"
                    >
                      {renderCopyButton('Secret', secret)}
                    </div>
                  </div>
                </Container>
              </div>}
            {renderEnvVars(['AUTH_SECRET'])}
          </ChecklistRow>
          <ChecklistRow
            title="Setup admin user"
            status={hasAdminUser}
          >
            Store admin email/password
            {' '}
            in environment variables:
            {renderEnvVars([
              'ADMIN_EMAIL',
              'ADMIN_PASSWORD',
            ])}
          </ChecklistRow>
        </Checklist>
        <Checklist
          title="Content"
          icon={<BiPencil size={16} />}
        >
          <ChecklistRow
            title="Configure domain"
            status={hasDomain}
            showWarning
          >
            {!hasDomain &&
              renderWarning({message:
                'Not explicitly setting a domain may cause ' + 
                'certain features to behave unexpectedly',
              })}
            Store in environment variable (seen in top-right nav):
            {renderEnvVars(['NEXT_PUBLIC_SITE_DOMAIN'])}
          </ChecklistRow>
          <ChecklistRow
            title="Add title"
            status={hasTitle}
            optional
          >
            Store in environment variable (seen in browser tab):
            {renderEnvVars(['NEXT_PUBLIC_SITE_TITLE'])}
          </ChecklistRow>
          <ChecklistRow
            title="Add description"
            status={hasDescription}
            optional
          >
            Store in environment variable (seen in nav, under title):
            {renderEnvVars(['NEXT_PUBLIC_SITE_DESCRIPTION'])}
          </ChecklistRow>
          <ChecklistRow
            title="Add about"
            status={hasAbout}
            optional
          >
            Store in environment variable (seen in grid sidebar):
            {renderEnvVars(['NEXT_PUBLIC_SITE_ABOUT'])}
          </ChecklistRow>
        </Checklist>
        {!simplifiedView && <>
          <Checklist
            title="AI text generation"
            titleShort="AI"
            icon={<HiSparkles />}
            experimental
            optional
          >
            <ChecklistRow
              title={isAiTextGenerationEnabled && isTestingConnections
                ? 'Testing OpenAI connection'
                : 'Add OpenAI secret key'}
              status={isAiTextGenerationEnabled}
              isPending={isAiTextGenerationEnabled && isTestingConnections}
              optional
            >
              {aiError && renderError({
                connection: { provider: 'OpenAI', error: aiError},
              })}
              Store your OpenAI secret key in order to add experimental support
              for AI-generated text descriptions and enable an invisible field
              called {'"Semantic Description"'} used to support CMD-K search:
              {renderEnvVars(['OPENAI_SECRET_KEY'])}
            </ChecklistRow>
            <ChecklistRow
              title={hasVercelKv && isTestingConnections
                ? 'Testing KV connection'
                : 'Enable rate limiting'}
              status={hasVercelKv}
              isPending={hasVercelKv && isTestingConnections}
              optional
            >
              {kvError && renderError({
                connection: { provider: 'Vercel KV', error: kvError},
              })}
              {renderLink(
                // eslint-disable-next-line max-len
                'https://vercel.com/docs/storage/vercel-kv/quickstart#create-a-kv-database',
                'Create Vercel KV store',
              )}
              {' '}
              and connect to project in order to enable rate limiting
            </ChecklistRow>
            <ChecklistRow
              // eslint-disable-next-line max-len
              title={`Auto-generated fields: ${aiTextAutoGeneratedFields.join(', ')}`}
              status={hasAiTextAutoGeneratedFields}
              optional
            >
              Comma-separated fields to auto-generate when
              uploading photos. Accepted values: title, caption,
              tags, description, all, or none (default is {'"all"'}):
              {renderEnvVars(['AI_TEXT_AUTO_GENERATED_FIELDS'])}
            </ChecklistRow>
          </Checklist>
          <Checklist
            title="Settings"
            icon={<BiCog size={16} />}
            optional
          >
            <ChecklistRow
              title="Grid homepage"
              status={isGridHomepageEnabled}
              optional
            >
              Set environment variable to {'"1"'} to show grid layout
              on homepage:
              {renderEnvVars(['NEXT_PUBLIC_GRID_HOMEPAGE'])}
            </ChecklistRow>
            <ChecklistRow
              title={`Default theme: ${defaultTheme}`}
              status={hasDefaultTheme}
              optional
            >
              {'Set environment variable to \'light\' or \'dark\''}
              {' '}
              to configure initial theme
              {' '}
              (defaults to {'\'system\''}):
              {renderEnvVars(['NEXT_PUBLIC_DEFAULT_THEME'])}
            </ChecklistRow>
            <ChecklistRow
              title="Preserve original uploads"
              status={areOriginalUploadsPreserved}
              optional
            >
              Set environment variable to {'"1"'} to prevent
              image uploads being optimized before storing:
              {renderEnvVars(['NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS'])}
            </ChecklistRow>
            <ChecklistRow
              title="Static optimization"
              status={isStaticallyOptimized}
              optional
              experimental
            >
              Set environment variable to {'"1"'} to enable static optimization,
              i.e., rendering pages and images at build time:
              {renderSubStatusWithEnvVar(
                arePhotosStaticallyOptimized ? 'checked' : 'optional',
                'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS',
              )}
              {renderSubStatusWithEnvVar(
                arePhotoOGImagesStaticallyOptimized ? 'checked' : 'optional',
                'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES',
              )}
              {renderSubStatusWithEnvVar(
                arePhotoCategoriesStaticallyOptimized ? 'checked' : 'optional',
                'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES',
              )}
            </ChecklistRow>
            <ChecklistRow
              title="Photo matting"
              status={arePhotosMatted}
              optional
            >
              Set environment variable to {'"1"'} to constrain the size
              {' '}
              of each photo, and enable a surrounding border:
              {renderEnvVars(['NEXT_PUBLIC_MATTE_PHOTOS'])}
            </ChecklistRow>
            <ChecklistRow
              title="Image blur"
              status={isBlurEnabled}
              optional
            >
              Set environment variable to {'"1"'} to prevent
              image blur data being stored and displayed:
              {renderEnvVars(['NEXT_PUBLIC_BLUR_DISABLED'])}
            </ChecklistRow>
            <ChecklistRow
              title="Geo privacy"
              status={isGeoPrivacyEnabled}
              optional
            >
              Set environment variable to {'"1"'} to disable
              collection/display of location-based data:
              {renderEnvVars(['NEXT_PUBLIC_GEO_PRIVACY'])}
            </ChecklistRow>
            <ChecklistRow
              title="Show repo link"
              status={showRepoLink}
              optional
            >
              Set environment variable to {'"1"'} to hide footer link:
              {renderEnvVars(['NEXT_PUBLIC_HIDE_REPO_LINK'])}
            </ChecklistRow>
            <ChecklistRow
              title="Public downloads"
              status={arePublicDownloadsEnabled}
              optional
            >
              Set environment variable to {'"1"'} to enable
              public photo downloads for all visitors:
              {renderEnvVars(['NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS'])}
            </ChecklistRow>
            <ChecklistRow
              title="Public API"
              status={isPublicApiEnabled}
              optional
            >
              Set environment variable to {'"1"'} to enable
              a public API available at <code>/api</code>:
              {renderEnvVars(['NEXT_PUBLIC_PUBLIC_API'])}
            </ChecklistRow>
            <ChecklistRow
              title="Priority order"
              status={isPriorityOrderEnabled}
              optional
            >
              Set environment variable to {'"1"'} to prevent
              priority order photo field affecting photo order:
              {renderEnvVars(['NEXT_PUBLIC_IGNORE_PRIORITY_ORDER'])}
            </ChecklistRow>
            <ChecklistRow
              title="Show social"
              status={showSocial}
              optional
            >
              Set environment variable to {'"1"'} to hide
              {' '}
              X button from share modal:
              {renderEnvVars(['NEXT_PUBLIC_HIDE_SOCIAL'])}
            </ChecklistRow>
            <ChecklistRow
              title="Show Fujifilm simulations"
              status={showFilmSimulations}
              optional
            >
              Set environment variable to {'"1"'} to prevent
              simulations showing up in /grid sidebar and
              CMD-K results:
              {renderEnvVars(['NEXT_PUBLIC_HIDE_FILM_SIMULATIONS'])}
            </ChecklistRow>
            <ChecklistRow
              title="Show EXIF data"
              status={showExifInfo}
              optional
            >
              Set environment variable to {'"1"'} to hide EXIF data:
              {renderEnvVars(['NEXT_PUBLIC_HIDE_EXIF_DATA'])}
            </ChecklistRow>
            <ChecklistRow
              title={`Grid aspect ratio: ${gridAspectRatio}`}
              status={hasGridAspectRatio}
              optional
            >
              Set environment variable to any number to enforce aspect ratio
              {' '}
              (default is {'"1"'}, i.e., square)—set to {'"0"'} to disable:
              {renderEnvVars(['NEXT_PUBLIC_GRID_ASPECT_RATIO'])}
            </ChecklistRow>
            <ChecklistRow
              title={`Grid density: ${gridDensity ? 'low' : 'high'}`}
              status={hasGridDensityPreference}
              optional
            >
              Set environment variable to {'"1"'} to ensure large thumbnails
              on photo grid views (if not configured, density is based on
              aspect ratio configuration):
              {renderEnvVars(['NEXT_PUBLIC_SHOW_LARGE_THUMBNAILS'])}
            </ChecklistRow>
            <ChecklistRow
              title="Legacy OG text alignment"
              status={isOgTextBottomAligned}
              optional
            >
              Set environment variable to {'"BOTTOM"'} to
              keep OG image text bottom aligned (default is {'"top"'}):
              {renderEnvVars(['NEXT_PUBLIC_OG_TEXT_ALIGNMENT'])}
            </ChecklistRow>
          </Checklist>
        </>}
      </div>
      <div className="pl-11 pr-2 sm:pr-11 mt-4 md:mt-7">
        <div>
          Changes to environment variables require a redeploy
          or reboot of local dev server
        </div>
        {!simplifiedView &&
          <div className="text-dim before:content-['—']">
            <div className="flex whitespace-nowrap">
              <span className="font-bold">Domain</span>
              &nbsp;&nbsp;
              <span className="w-full flex overflow-x-auto">
                {baseUrl || 'Not Defined'}
              </span>
            </div>
            <div>
              <span className="font-bold">Commit</span>
              &nbsp;&nbsp;
              {commitSha
                ? <span title={commitMessage}>{commitSha}</span>
                : 'Not Found'}
            </div>
          </div>}
      </div>
    </div>
  );
}
