import { useEffect, useRef, useState } from 'react';
import { z } from 'zod/mini';

import type { FormApi, FormOnSubmit, FormValues } from '~/src/components/Form';
import type {
  CreateFlowEmitter,
  CreateFlowStage,
} from '../../components/CreateFlow/types';

import Box from '~/src/components/Box';
import Form from '~/src/components/Form';
import Loading from '~/src/components/Loading';
import { DetailsForm } from '~/src/features/contentLink/components';
import useFetchSessionUser from '~/src/hooks/useFetchSessionUser';
import { useListenOnEvent } from '~/src/hooks/useListenOnEvent';
import uploadImage from '~/src/lib/image/uploadImage';
import { getPageMetadataApi } from '~/src/lib/pageMetadata';
import { useAppRouter } from '~/src/lib/router2';

interface FormData extends FormValues {
  title: string;
  domain: string;
  path: string;
}

const detailsStageQuerySchema = z.object({
  destinationUrl: z.string(),
  artistId: z.coerce.number().check(z.positive()),
  artistPath: z.string(),
  title: z.catch(z.optional(z.string()), undefined),
  imageUrl: z.catch(z.optional(z.string()), undefined),
  domain: z.catch(z.optional(z.coerce.number()), undefined),
  path: z.catch(z.optional(z.string()), undefined),
});

export const DetailsStage: CreateFlowStage = ({ events }) => {
  const router = useAppRouter();
  const query = router.getQuery(detailsStageQuerySchema);

  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const getMetadata = async () => {
      try {
        const { title, image } = await getPageMetadataApi(query.destinationUrl);
        await router.setQuery(
          { title, imageUrl: image ?? undefined },
          { type: 'replace' }
        );
        // noop on error, metadata is not too important to block user from continuing the flow
      } catch {}

      setIsLoading(false);
    };

    // already fetched or custom values provided, skip fetching metadata
    if (query.title) {
      setIsLoading(false);
    } else {
      // pulls metadata for the destination url
      getMetadata();
    }

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  if (isLoading) return <Loading />;
  return <DetailsContent events={events} />;
};

const DetailsContent = ({ events }: { events: CreateFlowEmitter }) => {
  const [isLoading, setIsLoading] = useState(false);

  const { userAccountId } = useFetchSessionUser();
  const formApiRef = useRef<FormApi>(null);
  const router = useAppRouter();
  const query = router.getQuery(detailsStageQuerySchema);

  const handleImageUpload = async (file?: File) => {
    if (!file) return;

    setIsLoading(true);

    try {
      const image = await uploadImage({
        file,
        scaleToWidth: 1200,
        asType: 'jpeg',
        scaleBeforeUpload: false,
        accountId: userAccountId,
      });

      return image.url;
    } finally {
      setIsLoading(false);
    }
  };

  const handleSubmit: FormOnSubmit<FormData> = async ({ values, form }) => {
    const imageFile = form.image.files[0];
    const imageUrl = (await handleImageUpload(imageFile)) ?? query.imageUrl;

    events.emit('submit', {
      title: values.title,
      domain: values.domain,
      path: values.path,
      imageUrl,
    });
  };

  useListenOnEvent(events, 'nextButtonPressed', () => {
    formApiRef.current?.submit();
  });

  return (
    <Box maxWidth="64rem" width="100%">
      <Form<FormData>
        testId="contentLinkDetailsForm"
        trackingId="contentLinkDetailsForm"
        apiRef={formApiRef}
        onSubmit={handleSubmit}
      >
        <DetailsForm
          initialValues={{
            imageUrl: query.imageUrl,
            title: query.title,
            domainId: query.domain,
            path: query.path,
          }}
          artistId={query.artistId}
          artistPath={query.artistPath}
          isLoading={isLoading}
          onImageReset={async () => {
            await router.setQuery(
              { ...query, imageUrl: undefined },
              { type: 'replace' }
            );
          }}
        />
      </Form>
    </Box>
  );
};
