'use client';

import { ControllerRenderProps, useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { AppRouterOutput } from '@/server/trpc/types';
import { PollQuestionType } from '@/server/modules/poll/types/poll-question-type.enum';

import { Form, FormField, FormItem, FormLabel } from '@/components/ui/form';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { Checkbox } from '@/components/ui/checkbox';
import { Textarea } from '@/components/ui/textarea';
import { Slider } from '@/components/ui/slider';
import { Button } from '@/components/ui/button';
import { FormMessage } from '@/components/form';
import { useCallback, useMemo } from 'react';
import { Label } from '@/components/ui/label';
import { api } from '@/lib/trpc/client';
import { Loader2 } from 'lucide-react';

type Poll = NonNullable<AppRouterOutput['poll']['get']>;
type PollFormValues = Record<string, string | string[] | number | undefined>;

type RadioParams = Poll['questions'][number]['params'] & {
  options: { label: string; value: string }[];
};

type SliderParams = Poll['questions'][number]['params'] & {
  min: number;
  max: number;
  step?: number;
};

const createPollSchema = (poll: Poll) => {
  const shape: Record<string, z.ZodTypeAny> = {};

  for (const q of poll.questions) {
    switch (q.type) {
      case PollQuestionType.RADIO:
        shape[q.id] = z.string().min(1, 'Påkrævet');
        break;

      case PollQuestionType.CHECKBOX:
        shape[q.id] = z.array(z.string()).min(1, 'Vælg mindst én mulighed');
        break;

      case PollQuestionType.SLIDER:
        shape[q.id] = z.number();
        break;

      case PollQuestionType.TEXTAREA:
        shape[q.id] = z.string().optional();
        break;
    }
  }

  return z.object(shape);
};

export default function PollForm({
  poll,
  setResult
}: {
  poll: Poll;
  setResult: (result: Poll['questions']) => void;
}) {
  const defaultValues = poll.questions.reduce<PollFormValues>((acc, q) => {
    switch (q.type) {
      case PollQuestionType.SLIDER:
        const { min, max, step = 1 } = q.params as SliderParams;
        const middle = min + Math.round((max - min) / (2 * step)) * step;
        acc[q.id] = middle;
        break;

      case PollQuestionType.CHECKBOX:
        acc[q.id] = [];
        break;

      case PollQuestionType.RADIO: {
        const options = (q.params as RadioParams).options;
        acc[q.id] = options[0]?.value;
        break;
      }
    }
    return acc;
  }, {});

  const form = useForm({
    resolver: zodResolver(createPollSchema(poll)),
    defaultValues
  });

  const { mutateAsync, isPending } = api.poll.response.useMutation({
    onSuccess(pollQuestion) {
      setResult(pollQuestion);
    }
  });

  const onSubmit = useCallback(
    async (values: PollFormValues) => {
      const formData = {
        pollId: poll.id,
        answer: poll.questions.flatMap((question) => {
          const value = values[question.id];

          if (
            question.type === PollQuestionType.CHECKBOX &&
            Array.isArray(value)
          ) {
            return value?.map((v: string) => ({
              questionId: question.id,
              answer: v
            }));
          }

          return [
            {
              questionId: question.id,
              answer: String(value)
            }
          ];
        })
      };

      await mutateAsync(formData);
    },
    [mutateAsync, poll]
  );

  return (
    <Form {...form}>
      <form
        onSubmit={form.handleSubmit(onSubmit)}
        className='space-y-4 md:space-y-6'
      >
        {poll.questions.map((question) => (
          <FormField
            disabled={isPending}
            key={question.id}
            control={form.control}
            name={question.id}
            render={({ field }) => (
              <FormItem className='space-y-2 border-l border-l-neutral-400 bg-white p-3 md:p-6'>
                <FormLabel className='font-serif text-lg font-bold'>
                  {question.title}
                </FormLabel>

                {question.type === PollQuestionType.RADIO && (
                  <RadioGroup
                    value={field.value}
                    onValueChange={field.onChange}
                  >
                    {(question.params as RadioParams).options.map((opt) => (
                      <div key={opt.value} className='flex items-center gap-2'>
                        <RadioGroupItem
                          value={opt.value}
                          className='size-6 border-neutral-500 text-neutral-500 [&_svg]:size-4 [&_svg]:fill-neutral-500'
                          id={opt.value}
                        />
                        <Label
                          className='font-serif text-lg text-neutral-800'
                          htmlFor={opt.value}
                        >
                          {opt.label}
                        </Label>
                      </div>
                    ))}
                  </RadioGroup>
                )}

                {question.type === PollQuestionType.CHECKBOX && (
                  <div className='space-y-2'>
                    {(question.params as RadioParams).options.map((opt) => (
                      <div key={opt.value} className='flex items-center gap-2'>
                        <Checkbox
                          id={opt.value}
                          className='size-6 rounded-none border-neutral-400 data-[state=checked]:border-neutral-500 data-[state=checked]:bg-neutral-50 data-[state=checked]:text-neutral-500 [&_svg]:size-5.5'
                          variant='x'
                          checked={field.value?.includes(opt.value)}
                          onCheckedChange={(checked) => {
                            const values = new Set<string>(
                              Array.isArray(field.value) ? field.value : []
                            );
                            if (checked) {
                              values.add(opt.value);
                            } else {
                              values.delete(opt.value);
                            }

                            field.onChange(Array.from(values));
                          }}
                        />
                        <Label
                          className='font-serif text-lg text-neutral-800'
                          htmlFor={opt.value}
                        >
                          {opt.label}
                        </Label>
                      </div>
                    ))}
                  </div>
                )}

                {question.type === PollQuestionType.SLIDER && (
                  <PollSlider
                    field={field}
                    question={question.params as SliderParams}
                  />
                )}

                {question.type === PollQuestionType.TEXTAREA && (
                  <Textarea
                    {...field}
                    rows={1}
                    className='min-h-8 rounded-none border-t-0 border-r-0 border-l-0 border-b-neutral-500 shadow-none'
                  />
                )}

                <FormMessage name={question.id} />
              </FormItem>
            )}
          />
        ))}

        <Button
          type='submit'
          className='ml-auto flex items-center gap-2'
          size='lg'
          disabled={isPending}
        >
          {isPending && <Loader2 className='size-5 animate-spin' />}
          {isPending ? 'sender' : 'indsend'}
        </Button>
      </form>
    </Form>
  );
}

function PollSlider({
  field,
  question
}: {
  field: ControllerRenderProps;
  question: SliderParams;
}) {
  const { min, max, step = 1 } = question;

  const value = field.value as number;

  const steps = useMemo(() => {
    const result: number[] = [];
    for (let v = min; v <= max; v += step) {
      result.push(v);
    }
    return result;
  }, [min, max, step]);

  return (
    <div className='mt-2'>
      <Slider
        min={min}
        max={max}
        step={step}
        value={[value]}
        onValueChange={([v]) => field.onChange(v)}
        className='[&_[role=slider]]:border-client-red [&_[role=slider]]:bg-client-red [&_[role=slider]]:focus:ring-client-red z-1 [&_[data-orientation=horizontal]]:bg-transparent [&_[role=slider]]:size-6 [&_[role=slider]]:rounded-full [&_[role=slider]]:hover:bg-red-700 [&_[role=slider]]:focus:ring-2 [&_[role=slider]]:focus:ring-offset-2'
      />

      <div className='relative mx-3 -mt-3 flex justify-between before:absolute before:inset-0 before:top-2 before:z-0 before:h-1 before:bg-neutral-200'>
        {steps.map((stepValue) => (
          <div key={stepValue} className='flex max-w-px flex-col items-center'>
            <div className='z-0 h-5 w-1 bg-neutral-200' />

            <span
              className={`mt-2 text-lg break-normal ${
                stepValue === value
                  ? 'text-client-red font-semibold'
                  : 'text-black'
              }`}
            >
              {stepValue}
            </span>
          </div>
        ))}
      </div>
    </div>
  );
}
